1. 从"动效总览"这个需求说起:为什么我盯上了 Remotion Skill
做视频动效的人大概都有过这种体验:一个项目里要展示十几个功能模块,每个模块单独录屏太碎,串成一条长视频又没人看得下去,最后往往妥协成一张静态长图或者一份PDF。但静态图有个致命问题——它没法体现"动"的部分,而很多功能的价值恰恰藏在动效里。比如一个转场、一个数据增长的动画、一个按钮的微交互,这些用静态图根本讲不清楚。
我最初接触 Remotion 是因为想用代码批量生成这类"总览型"动效视频。Remotion 的核心思路很直接:用 React 组件描述视频的每一帧,然后通过渲染管线把组件树输出成 MP4。这意味着视频不再是时间轴上的手工活,而是可以复用、可以参数化、可以版本管理的代码产物。对于"动效总览"这种需要统一风格、批量套用模板的场景,这个特性简直是量身定做。
但真正让我决定深入的是标题里提到的"Skill"和"Codex+Claude Code全部通用"。这里的 Skill 指的是一套封装好的能力包,它把 Remotion 的常见动效模式抽象成了可调用的技能单元;而"通用"则意味着这套东西不绑定某个特定的代码助手,你在不同的 AI 编程环境里都能用同一套逻辑去驱动它。换句话说,它解决的是"我知道要做什么动效,但每次都要从零写组件"的重复劳动问题。
这篇文章适合三类人看:一是做产品演示、功能总览视频的运营和设计同学,你们可能不写代码但需要理解这套流程能产出什么;二是前端或全栈开发者,你们想用代码化的方式替代手工剪辑;三是已经在用 AI 辅助编程的人,你们想知道怎么把 Remotion Skill 接入自己的工作流。我会从动效总览的设计思路讲起,一路拆到 Skill 的封装逻辑、跨工具通用的实现细节,以及我在实际渲染中踩过的坑。
2. 动效总览到底在"总览"什么:需求拆解与视觉语言设计
2.1 总览视频的信息密度陷阱
很多人做总览视频的第一个误区是"什么都想放"。一个产品有二十个功能,就恨不得二十个都平均用力,结果视频变成流水账,观众看到第八个就划走了。我在做第一个版本的时候就犯了这个错,三分钟的视频里塞了十八个模块,每个模块停留十秒,配上一模一样的淡入淡出。后来拿给几个同事看,反馈出奇一致:"记不住任何一个。"
问题出在信息密度上。总览视频的本质不是"展示全部",而是"建立认知地图"。观众看完应该能说出"这个产品大概有哪几块能力",而不是"它有一个按钮点了会变色"。所以我在重新设计时做了三件事:第一,把功能按主题聚合成四到六个"能力域",每个域用一个代表性动效来代言;第二,给每个域分配不同的视觉节奏,有的快有的慢,形成呼吸感;第三,在域与域之间加入明确的过渡标识,让观众知道"这一块讲完了,下一块开始"。
这个思路落到 Remotion 里,就变成了一个分层结构:最外层是时间轴编排,决定每个能力域的出现顺序和时长;中间层是每个域的动效组件,负责具体的视觉表现;最内层是原子动效单元,比如数字滚动、路径描边、卡片翻转,这些是可以跨域复用的。分层的好处是,当你需要调整某个域的时长时,不用动里面的动效逻辑;当你需要换一种数字滚动效果时,所有用到它的地方一起更新。
2.2 用"节奏曲线"代替均匀分配
均匀分配时长是总览视频的另一个隐形杀手。人的注意力天然有起伏,如果每个片段都是等长的,大脑很快就会进入"自动忽略"模式。我在 Remotion 里引入了一个叫"节奏曲线"的概念,其实就是给每个能力域定义一个权重值,然后按权重分配总时长。
具体做法是:先确定视频总长,比如 90 秒;然后给每个能力域打分,核心功能打 3 分,辅助功能打 1 分,过渡片段打 0.5 分;最后按比例换算成帧数。这样核心功能自然获得更多展示时间,辅助功能快速带过,整体节奏就有了轻重缓急。在 Remotion 的代码里,这体现为一个配置数组,每个元素包含能力域名称、权重、对应的动效组件和文案。渲染时遍历这个数组,累加帧数偏移量,就能精确控制每个片段的起止帧。
提示:权重分配不要凭感觉,最好找两三个目标用户做一次快速测试,让他们看完后复述记住了什么。如果某个你认为是核心的功能没人提,要么是权重不够,要么是动效表现力不足。
2.3 视觉语言的一致性从哪来
总览视频最怕"拼凑感"——这个片段是扁平风,那个片段是拟物风,颜色也不统一。Remotion 的优势在于,你可以把设计系统直接编码进去。我通常会定义一个主题对象,里面包含主色、辅色、背景色、字体族、圆角半径、阴影参数、动效缓动函数等。所有动效组件都从这个主题对象里取值,而不是各自写死。
这样做还有一个额外好处:换肤成本极低。如果品牌色从蓝色换成橙色,只需要改主题对象里的一个字段,整条视频重新渲染即可。我在一个项目里做过测试,从修改主题到产出新视频,全程不到五分钟,其中渲染占了大头。这种效率是手工剪辑很难达到的。
缓动函数的选择也值得单独说。Remotion 内置了Easing模块,但默认的线性缓动会让动效显得很"机械"。我一般会用Easing.bezier(0.25, 0.1, 0.25, 1)作为标准缓动,也就是常说的 ease 曲线;对于需要"弹一下"的效果,用Easing.bezier(0.34, 1.56, 0.64, 1)这种带过冲的贝塞尔。关键是全片统一,不要这个片段用 ease,那个片段用 linear,观众虽然说不出来哪里怪,但能感觉到不协调。
3. Remotion Skill 的封装逻辑:把动效模式变成可调用单元
3.1 为什么需要 Skill 这一层抽象
直接用 Remotion 写动效,你会反复遇到同一类代码:一个元素从透明到不透明、从位移到归位、从缩放到正常。如果每个动效都从头写interpolate和spring,代码会变得又长又重复。Skill 这一层要解决的就是这个问题——把常见的动效模式封装成带参数的函数或组件,调用时只需要传"从哪到哪、持续多久、什么缓动"。
我理解的 Skill 不是某个官方概念,而是一种工程实践。它的形态可以是一个 React Hook,比如useFadeIn(delay, duration);也可以是一个高阶组件,比如<SlideIn direction="left" distance={100}>;还可以是一个纯函数,比如getScaleInterpolation(frame, start, end)。关键不在于形式,而在于它把"动效意图"和"动效实现"分开了。你描述的是"这个卡片要从右边滑进来",而不是"在第 30 帧到第 60 帧之间,x 坐标从 1920 插值到 960"。
这种分离带来的直接好处是可组合性。一个"卡片滑入"的 Skill 可以嵌套一个"文字淡入"的 Skill,再嵌套一个"图标旋转"的 Skill,最终形成一个复杂的复合动效,但每一层都是独立可测试的。我在实际项目里维护了一个大约二十个 Skill 的库,覆盖了淡入淡出、位移、缩放、旋转、路径描边、数字滚动、进度条、打字机效果等常见需求。新项目启动时,直接引入这个库,半天就能搭出总览视频的骨架。
3.2 Skill 的参数设计:少即是多
设计 Skill 参数时最容易犯的错是"参数爆炸"。一个淡入效果,如果开放了起始透明度、结束透明度、起始帧、结束帧、缓动函数、延迟、重复次数、方向等十几个参数,调用者反而不知道该怎么填。我的经验是,每个 Skill 只暴露三到四个核心参数,其余用合理的默认值。
以useFadeIn为例,我只暴露三个参数:delay(延迟多少帧开始)、duration(持续多少帧)、from(起始透明度,默认 0)。缓动函数固定为 ease,因为淡入用其他缓动反而奇怪。如果某个特殊场景确实需要自定义缓动,再提供一个高级版本useFadeInAdvanced,把完整参数暴露出来。这样 90% 的场景用简单版,10% 的特殊需求用高级版,代码可读性大幅提升。
另一个技巧是用"语义化参数"代替"数值参数"。比如位移 Skill 不传x和y的像素值,而是传direction: 'left' | 'right' | 'up' | 'down'和distance: 'small' | 'medium' | 'large'。内部再把语义映射成具体像素,比如 small 对应 40px,medium 对应 100px,large 对应 200px。这样做的好处是,当设计规范调整时,只需要改映射表,所有调用处自动生效。
3.3 跨工具通用的关键:不依赖特定运行时
标题里说"Codex+Claude Code全部通用",我理解这里的"通用"有两层含义。第一层是 Skill 本身不依赖某个特定的 AI 助手,它就是普通的 TypeScript 代码,放在任何支持 React 的环境里都能跑。第二层是这套 Skill 的描述方式足够清晰,以至于不同的代码助手都能理解它的用法并正确调用。
要做到第一层,关键是避免使用任何平台特有的 API。比如不要用某个助手提供的"代码生成标记",也不要把 Skill 和某个特定的项目结构绑定。我的做法是把 Skill 库做成一个独立的 npm 包,有完整的类型定义和 README,任何项目npm install之后就能用。这样无论你是在哪个编辑器里写代码,无论你用哪个助手补全,产出的代码都是一样的。
要做到第二层,关键是给每个 Skill 写清楚"意图描述"。我在每个 Skill 的 JSDoc 里都会写一段自然语言说明,比如"这个 Hook 让元素在指定延迟后淡入,持续指定帧数,适用于标题、卡片、图标等需要柔和出现的场景"。这段描述不仅是给人看的,也是给代码助手看的——当助手读到这段注释时,它更容易在合适的场景推荐这个 Skill,而不是让开发者手写 interpolate。
注意:如果你把 Skill 库发布到公共仓库,记得在 package.json 里把
sideEffects设为 false,并且提供 ESM 和 CJS 双格式产物。这样无论你的项目用哪种模块系统,都能正常引入,不会出现"在 A 项目能用,在 B 项目报错"的情况。
4. 从零搭一条总览视频:完整实操链路
4.1 环境准备与项目初始化
先说环境。Remotion 对 Node 版本有要求,我一般用当前 LTS 版本,太老的版本会在安装依赖时出各种奇怪问题。初始化项目最省事的方式是用官方脚手架,但如果你像我一样喜欢自己控制结构,也可以手动建。手动建的话,核心依赖就三个:remotion、@remotion/cli、react和react-dom。TypeScript 项目再加typescript和@types/react。
项目结构我习惯这样组织:src/skills放 Skill 库,src/compositions放具体的视频组合,src/theme放主题配置,src/assets放字体和图片。remotion.config.ts里配置视频尺寸、帧率、并发数等。帧率我一般用 30,因为大多数动效在 30 帧下已经足够流畅,渲染时间也比 60 帧少一半。尺寸根据发布平台定,横屏用 1920x1080,竖屏用 1080x1920,方形用 1080x1080。
初始化完成后,先跑一个最简单的 Composition 验证环境。创建一个只显示一行文字的组件,用useCurrentFrame拿到当前帧,用interpolate让透明度从 0 变到 1。然后执行npx remotion studio,在浏览器里预览。如果能看到文字淡入,说明环境没问题。这一步看似简单,但能帮你排除掉 80% 的环境问题,比如版本冲突、路径错误、依赖缺失。
4.2 用 Skill 组装第一个能力域
环境跑通后,开始组装第一个能力域。假设我们要展示"数据看板"这个功能,视觉上是一个卡片从右侧滑入,卡片内的数字从 0 滚动到目标值,同时底部有一条进度条从 0 走到 100%。
第一步,引入位移 Skill。调用<SlideIn direction="right" distance="medium" delay={0} duration={20}>包裹卡片组件。第二步,在卡片内部引入数字滚动 Skill。这个 Skill 接收from、to、delay、duration四个参数,内部用interpolate计算当前值,再用toFixed格式化。第三步,引入进度条 Skill,它本质上是一个宽度插值,从 0% 到 100%,配合一个圆角矩形。
这三个 Skill 嵌套在一起,代码量大概二十行,但产出的动效层次分明:卡片先滑入,数字随后开始滚动,进度条最后启动。每个 Skill 的延迟参数错开,形成"接力"效果。如果不用 Skill,同样的效果大概要写八十到一百行,而且每次调整都要改多处。
组装完成后,在 Studio 里逐帧检查。重点看三个地方:动效的起始帧和结束帧是否干净,有没有元素在不该出现的时候露出来;多个 Skill 的延迟是否协调,有没有出现"等太久"或"挤在一起"的感觉;缓动是否统一,有没有某个元素显得特别突兀。我一般会反复看五到十遍,直到每个过渡都顺眼为止。
4.3 能力域之间的过渡设计
单个能力域做好后,下一个问题是域与域之间怎么衔接。最省事的做法是硬切,但硬切在总览视频里显得很生硬。我试过几种过渡方案,最后固定用"遮罩擦除"加"轻微缩放"的组合。
具体实现是:在第一个能力域结束前 15 帧,启动一个全屏遮罩,从屏幕一侧扫到另一侧,遮罩颜色用主题背景色。遮罩扫过的区域,旧内容被覆盖,新内容在遮罩后面已经就位。同时,旧内容整体缩小到 0.95 倍并轻微透明,新内容从 1.05 倍恢复到 1 倍。这样视觉上像是"翻页",但比翻页更柔和。
这个过渡本身也可以封装成一个 Skill,叫useTransition,接收type(擦除方向)、duration、delay三个参数。在时间轴编排时,每个能力域的结束帧减去过渡时长,就是下一个能力域的开始帧。这样整个视频的时间轴就是一条连续的线,不会出现空帧或重叠。
提示:过渡时长不要超过 20 帧,否则会显得拖沓。15 帧左右是比较舒服的区间,既能让观众感知到切换,又不会打断节奏。
4.4 渲染输出与参数调优
所有能力域组装完成后,进入渲染环节。Remotion 的渲染命令是npx remotion render <composition-id> <output-path>。默认情况下,它会用 CPU 渲染,速度取决于机器性能。如果视频较长或分辨率较高,可以考虑开启 GPU 渲染,但需要确认你的环境支持。
渲染参数里我比较关注三个:--concurrency控制并发数,默认是 CPU 核心数的一半,如果内存充足可以调高,但不要超过核心数;--crf控制视频质量,数值越小质量越高文件越大,我一般用 18 到 23 之间;--pixel-format控制像素格式,默认是 yuv420p,兼容性最好,如果要做后期合成可以用 yuv444p 保留更多色彩信息。
渲染完成后,一定要完整看一遍成片。Studio 里的预览和最终渲染有时会有细微差异,比如字体加载时机、图片解码顺序等。我遇到过一次预览正常但渲染后某个图标不显示的问题,排查发现是图标用了网络字体,渲染时网络请求超时。后来改成把字体文件打包进项目,问题就消失了。所以我的经验是:所有资源本地化,不要依赖运行时网络请求。
5. 跨工具通用的实操细节:让不同助手都能读懂你的 Skill
5.1 类型定义是最好的"说明书"
如果你希望不同的代码助手都能正确使用你的 Skill,最有效的手段不是写多长的文档,而是把类型定义写清楚。TypeScript 的类型系统本身就是一种精确的契约,助手在补全时会读取这些类型,从而知道该传什么参数、返回什么值。
举个例子,useFadeIn的类型定义如果写成(delay: number, duration: number) => number,助手只知道要传两个数字,但不知道单位是帧还是秒,也不知道返回的是什么。如果写成(options: { delayInFrames: number; durationInFrames: number }) => { opacity: number },助手就能准确理解:延迟和时长都是以帧为单位,返回值是一个包含 opacity 的对象。参数名里的InFrames后缀就是最好的注释。
我还会给每个 Skill 写一个example字段放在 JSDoc 里,比如@example useFadeIn({ delayInFrames: 0, durationInFrames: 30 })。这样助手在推荐这个 Skill 时,可以直接参考示例生成调用代码,减少出错概率。实测下来,有示例的 Skill 被正确调用的比例明显高于没有示例的。
5.2 避免"隐式约定",把假设写进代码
跨工具通用还有一个隐形障碍:隐式约定。比如你的 Skill 假设调用者已经设置了某个 Context,或者假设帧率是 30,或者假设主题对象已经初始化。这些假设在你自己的项目里没问题,但换一个助手、换一个项目,就可能出问题。
我的做法是把所有假设都显式化。如果 Skill 依赖主题,就在参数里要求传入主题对象,而不是从 Context 里偷偷取。如果 Skill 假设帧率,就在参数里加一个fps字段,默认 30,但允许覆盖。如果 Skill 需要某个前置条件,就在函数开头做检查,不满足时抛出明确的错误信息,而不是静默失败。
这样做虽然让调用稍微麻烦一点,但换来的是可移植性。我把 Skill 库从一个项目复制到另一个项目时,几乎不需要改动就能用,因为所有依赖都是显式的。助手在生成代码时,也会因为这些显式参数而更少犯错。
5.3 版本管理与向后兼容
Skill 库一旦被多个项目使用,版本管理就变得重要。我用的是语义化版本:修复 bug 升 patch,新增 Skill 升 minor,修改现有 Skill 的参数或行为升 major。每次升 major 时,我会在 CHANGELOG 里写清楚哪些 Skill 有破坏性变更,以及迁移方法。
向后兼容方面,我尽量用"新增而非修改"的策略。比如要给useFadeIn加一个新参数,我不会直接改它的签名,而是新增一个useFadeInV2,旧版保留并标记为 deprecated。这样老项目不受影响,新项目可以用新版。等所有项目都迁移完成后,再在下个 major 版本里移除旧版。
这个策略在跨工具场景下尤其重要,因为不同项目可能用不同版本的 Skill 库,如果某个助手按照旧版签名生成代码,而项目已经升级到新版,就会报错。保留旧版可以避免这种问题,给迁移留出缓冲期。
6. 踩坑记录:那些让我重渲染了十几次的问题
6.1 字体加载时机导致的"闪字"
第一个坑是字体。Remotion 渲染时,如果字体文件没有在渲染开始前加载完成,前几帧的文字会用 fallback 字体显示,然后突然切换成目标字体,形成"闪字"。这个问题在 Studio 预览时不容易发现,因为 Studio 会缓存字体,但渲染时每次都是全新环境。
解决方案是用@remotion/fonts提供的loadFont函数,在 Composition 的顶层调用,并配合delayRender和continueRender控制渲染时机。具体做法是:在组件挂载时调用delayRender()拿到一个 handle,然后异步加载字体,加载完成后调用continueRender(handle)。这样 Remotion 会等到字体就绪后才开始渲染,避免闪字。
我一开始偷懒,直接把字体文件放在public目录用 CSS@font-face引入,结果渲染时前几帧还是闪。后来改成loadFont才彻底解决。这个坑的教训是:渲染环境和浏览器环境有差异,不能假设 CSS 加载时序一致。
6.2 并发渲染下的内存溢出
第二个坑是内存。当视频较长、分辨率较高、并发数设置得比较大时,渲染进程可能因为内存不足而崩溃。我遇到过一次,渲染到一半报 "JavaScript heap out of memory",排查发现是并发数设成了 CPU 核心数,而每个渲染进程都要加载一份完整的组件树和资源。
解决方案有两个:一是降低并发数,从核心数降到核心数的一半甚至三分之一;二是给 Node 进程增加内存上限,通过NODE_OPTIONS=--max-old-space-size=4096这样的环境变量。我一般两个一起用,并发数设为 4,内存上限设为 4GB,基本能稳定跑完十分钟以内的 1080p 视频。
还有一个相关问题是资源重复加载。如果每个能力域都单独引入同一张图片或同一个字体,渲染时可能会重复加载。我的做法是把公共资源提升到顶层,用 React Context 或模块级缓存共享。这样每个渲染进程只加载一次,内存占用明显下降。
6.3 时间轴偏移的累积误差
第三个坑比较隐蔽:时间轴偏移的累积误差。当能力域数量较多时,如果每个域的时长都是通过浮点数累加计算的,最终的总帧数可能出现零点几帧的偏差,导致最后一个域被截断或出现空帧。
解决方案是全程用整数帧计算,避免浮点数。权重换算成帧数时,用Math.round取整,并且把取整后的值累加,而不是先累加再取整。另外,在时间轴编排的配置里,我加了一个校验函数,检查所有域的帧数之和是否等于总帧数,不等就抛出错误。这样在开发阶段就能发现问题,而不是等到渲染后才发现。
这个坑的教训是:视频渲染对帧的精度要求很高,任何浮点误差都可能被放大。能用整数就用整数,能校验就校验,不要依赖"应该没问题"。
6.4 跨平台字体渲染差异
第四个坑是跨平台差异。同样的代码,在开发机上渲染正常,换一台机器渲染出来字体粗细不一样,或者行高有细微偏移。这是因为不同操作系统的字体渲染引擎有差异,尤其是中文字体。
解决方案是尽量使用 Web 字体而不是系统字体,并且把字体文件打包进项目。如果必须用系统字体,就在渲染配置里指定字体回退链,并且在目标平台上做一次完整测试。我现在的习惯是:所有文字都用打包的 Web 字体,不依赖系统字体,这样无论在哪台机器上渲染,结果都一致。
注意:中文字体文件通常比较大,打包时记得做子集化,只保留用到的字符。否则一个字体文件可能几十 MB,拖慢渲染速度。
7. 一些让效率翻倍的小技巧
7.1 用"预览模式"快速迭代
Remotion Studio 的预览模式是我用得最多的功能。它支持热更新,改代码后浏览器自动刷新,不用重新渲染。对于动效调试来说,这个反馈速度至关重要。我一般会把视频拆成多个小 Composition,每个只包含一个能力域,这样预览时加载快,调试也聚焦。
另外,Studio 里可以拖动时间轴逐帧查看,配合useCurrentFrame的显示,能精确看到每一帧的状态。我调试动效时,会先把时间轴拖到关键帧,比如动效的起始帧、中间帧、结束帧,检查元素的位置、透明度、缩放是否符合预期。确认后再整体播放一遍看流畅度。
7.2 把常用配置抽成预设
总览视频的配置项很多:主题色、字体、圆角、阴影、缓动、时长权重等。如果每个项目都从头配一遍,既费时又容易不一致。我的做法是抽几套预设,比如"科技蓝"、"活力橙"、"简约灰",每套预设包含完整的主题配置和默认权重分配。新项目启动时,选一套预设,改几个字段就能用。
预设的另一个好处是团队协作。当多个人做不同项目的总览视频时,用同一套预设能保证视觉风格统一。如果某个项目需要微调,就在预设基础上覆盖,而不是从零开始。这样既有个性又有共性。
7.3 渲染前先做"静帧检查"
正式渲染前,我会先做一次"静帧检查":把时间轴拖到几个关键帧,截图保存,然后快速浏览这些截图。重点看三件事:文字有没有溢出容器、元素有没有重叠、颜色对比度是否足够。这些问题在动态播放时容易被忽略,但截图一看就很明显。
静帧检查能省下大量重渲染时间。一次完整渲染可能要几分钟到几十分钟,而静帧检查只要几十秒。我现在的流程是:静帧检查通过后再渲染,渲染后完整看一遍,基本一次过。以前不做静帧检查时,经常渲染完才发现问题,然后改代码、重新渲染,来回好几次。
7.4 用 Git 管理视频版本
视频也是代码,也应该用 Git 管理。我把整个 Remotion 项目纳入 Git,每次修改都提交,并且给重要的版本打 tag。这样当需要回退到某个版本时,直接 checkout 即可。配合 CI,还可以做到"提交即渲染",自动产出最新版本的视频。
Git 管理还有一个好处是 diff 可见。虽然视频文件本身是二进制的,但生成视频的代码是文本的,diff 能清楚看到改了哪些动效、哪些参数。这对于排查"为什么这版和上版不一样"特别有用。
8. 关于这套流程的适用边界
Remotion Skill 这套东西不是万能的。它最适合的场景是"结构化、模板化、需要批量产出"的动效视频,比如产品功能总览、数据报告动画、教学课件、营销素材。这些场景的共同特点是:视觉风格统一、内容可以参数化、需要频繁更新。
它不太适合的场景是"高度创意、每帧都要手工打磨"的片子,比如品牌宣传片、艺术短片。这类片子更依赖设计师的直觉和手工调整,代码化的收益不明显,反而可能限制创意。我一般会建议这类需求走传统剪辑流程,或者用 Remotion 做部分自动化,比如片头片尾的模板化部分。
另外,Remotion 的学习曲线主要在于 React 和动画概念,如果你完全没有前端基础,前期会比较吃力。但如果你已经会 React,那上手就很快,基本上看几个示例就能开始写。我的经验是,一个熟悉 React 的开发者,大概两三天就能独立做出一个像样的总览视频。
最后分享一个我自己的判断标准:如果一个视频需要做三遍以上,或者有十个以上的相似片段,那就值得用 Remotion Skill 来做。如果只是做一次性的、片段很少的视频,手工剪辑可能更快。工具是为人服务的,不要为了用工具而用工具。