Remotion
构建和维护 Remotion 项目,同时保留用户的更改。如果文件意外更改,除非用户另有确认,否则将其视为有意为之。
工作流程
- 检查
package.json、锁文件、现有合成、当前差异以及npx remotion versions。重用包管理器和项目结构;将所有remotion/@remotion/*包保持在一个确切的版本上。 - 确认所请求的输出:合成/源更改、Studio 预览、渲染的媒体、嵌入式 Player 或渲染服务。
- 实现确定性、帧驱动的视觉效果,并保持渲染输入可序列化。
- 在 Studio 中预览。仅当用户请求媒体文件时才渲染。
- 使用
ffprobe验证渲染的媒体,并检查代表性帧。 - 报告更改的文件、预览/渲染证据以及交付回执。在 Prismer 中,松散的沙盒路径不是已交付的结果。
Prismer 工件和交付合同
当用户请求渲染媒体时,将最终文件写入 $PRISMER_ARTIFACTS_DIR(调度 <artifacts_dir>)。将项目脚手架、帧、探针和其他中间文件放在 $PRISMER_SCRATCH_DIR 或现有项目树下。然后,将每个请求的最终文件精确交付一次:
cloud deliver "$PRISMER_ARTIFACTS_DIR/launch-video.mp4"
写入或渲染文件并不会附加它。成功的 cloud deliver 回执是交付的仲裁者。除非用户请求,否则不要交付预览帧、临时音频或重复编码。如果不存在调度工件目录,则将结果保留在用户批准的项目输出路径中,并报告交付不可用,而不是凭空捏造一个回执。
项目创建和合成
不要在不空的项目上进行脚手架搭建。当没有合适的项目存在时,确认 Node.js 和 Git 可用,然后运行:
npx create-video@latest --yes --blank --no-tailwind my-video
cd my-video
npm install
使用有意义的目录名称。仅当用户要求或项目已经使用 Tailwind 时才添加它。在继续之前,检查生成的清单、配置和全局 CSS:生成器版本即使请求了 --no-tailwind,也可能仍然发出 Tailwind 依赖和导入。仅当项目不使用它们时才移除它们,并保持 Remotion 包版本对齐。
在 Prismer 沙盒镜像中,对每个可能安装或调用缺失包的命令(包括脚手架搭建、npm ci、remotion add、探针和渲染)使用命令作用域的可写 npm 缓存:
npm_config_cache="${PRISMER_SCRATCH_DIR:-/tmp}/npm-cache" npm ci
不要为了绕过沙盒所有权不匹配而更改用户的全局 npm 配置。
保持静态合成元数据内联:
<Composition
id="LaunchVideo"
component={LaunchVideo}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
defaultProps={{title: 'Launch day'}}
/>
仅当持续时间、尺寸或属性确实依赖于输入、远程数据或媒体检查时才使用 calculateMetadata()。保持 defaultProps 内联,以便 Studio 可以将属性编辑保存回代码。对于多场景视频,使用命名的 <Sequence> 边界,并从单个 fps 感知的计划中推导所有时序。
帧驱动动画
使用 useCurrentFrame()、interpolate()、弹簧或缓动来驱动渲染动画。不要使用 CSS 过渡、CSS 动画、挂钟定时器或 Tailwind 动画工具;它们不是确定性渲染。
import {
AbsoluteFill,
Easing,
Interactive,
interpolate,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
export const Hero = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
return (
<AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
<Interactive.Div
name="Hero title"
style={{
opacity: interpolate(frame, [0, fps], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.16, 1, 0.3, 1),
}),
scale: interpolate(frame, [0, fps], [0.9, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.spring({damping: 200}),
output: 'perceptual-scale',
}),
fontSize: 88,
}}
>
Launch day
</Interactive.Div>
</AbsoluteFill>
);
};
优先使用单独的 scale、translate 和 rotate CSS 属性,而不是组合的 transform 字符串。除非有意外推,否则要夹紧有限动画的两端。
Studio 可编辑标记
使用 Interactive.* 来处理 HTML 和 SVG 元素,这些元素在 Studio 中应该可以被选中、拖动、调整大小、旋转、设置样式或添加关键帧。<Img> 已经是可交互的。
- 为每个交互元素提供一个简短、硬编码、描述性的
name。 - 将一次性文本直接放在元素内部。
- 当 Studio 写回很重要时,保持样式对象、
interpolate()范围、输出、缓动和外推内联。 - 只从
useVideoConfig()解构支持的值,例如fps、width、height和durationInFrames。 - 避免在可编辑的关键帧范围内部使用提取的样式常量、对象展开、计算出的效果数组和任意变量。
对于自定义组件,在实现之前,查阅当前官方 Remotion 文档中的 make-component-interactive API。
资产、媒体和时序
将本地资产放在 public/ 中,并使用 staticFile() 引用它们:
import {Audio, Video} from '@remotion/media';
import {AnimatedImage, CanvasImage, staticFile} from 'remotion';
export const MediaLayer = () => (
<>
<Video src={staticFile('clip.mp4')} />
<Audio src={staticFile('music.mp3')} />
<CanvasImage src={staticFile('logo.png')} />
<AnimatedImage src={staticFile('sticker.gif')} />
</>
);
当基于 Chrome 的动画图像路径不合适时,使用 @remotion/gif。使用 npx remotion add <package> 安装生态系统包,以使 Remotion 包版本保持一致。
对于可重现的沙盒渲染,优先使用本地或内容寻址的媒体。不要将 cookie、承载令牌、签名 URL 或地图凭证嵌入源文件或组合属性中。对于异步资产、字体、字幕和地图瓦片,使用 delayRender() / continueRender() 与 cancelRender() 配对,以便在失败时明确失败,而不是挂起到超时。
在支持的组件上使用 from、durationInFrames 和 trimBefore。将不支持的组件包装在 <Sequence> 中:
<Sequence name="Product demo" from={2 * fps} durationInFrames={6 * fps}>
<Video src={staticFile('demo.mp4')} trimBefore={1 * fps} />
</Sequence>
将媒体持续时间与预期的可用片段匹配。将场景时序保持在一个计划中,以免过渡意外重叠或留下空白帧。
对于效果,使用能产生镜头的最简单技术:
- HTML、SVG 和 CSS。
- 元素上的 Remotion 效果或
HtmlInCanvas包装器。 - 仅当前两种方法无法产生时才使用自定义效果或着色器。
保持效果数组稳定。渲染单独的元素,而不是有條件地更改效果数组的形状。
字幕和多媒体检查
将字幕规范化为 Remotion 的 Caption[] 形状,并将摄入时间戳保持为毫秒:
import type {Caption} from '@remotion/captions';
const caption: Caption = {
text: 'Hello',
startMs: 0,
endMs: 800,
timestampMs: null,
confidence: null,
};
仅在使用组合 fps 的组合边界处将毫秒转换为帧。在可用时保留单词时序,将字幕分组为可读页面,从帧时钟进行动画,并在实际输出尺寸下验证换行和安全区域。
对于 SRT,使用当前的 @remotion/captions 解析器,而不是编写时间戳解析器。在实现字幕之前,确认 parse-srt、createTikTokStyleCaptions 和所选转录提供商的当前 API。
对于音频可视化,使用 @remotion/media-utils 配合帧窗口音频数据;切勿从实时分析器驱动条形或波形。对于图表和文本动画,首先计算最终几何形状,然后使用基于帧的裁剪、不透明度或变换来显示它,以便条形、标签和字形布局保持稳定。
使用 Mediabunny 进行浏览器端持续时间、尺寸、裁剪、旋转、元数据和容器处理。在使用不熟悉的 API 之前,阅读 https://mediabunny.dev/llms.txt。保持 mediabunny 和 @mediabunny/* 版本与项目的 Remotion 版本兼容。
地图
为镜头选择一种技术:
| 技术 | 使用场景 |
|---|---|
| 准备好的光栅 | 确定性静态/卫星图像配合动画叠加就足够了。 |
| Mapbox | 需要精美的样式、地球渲染或 3D 建筑物。 |
| MapLibre | 首选不需要 Mapbox API 密钥的开源矢量运行时。 |
| MapTiler | 托管样式或地理注释适合该项目。 |
| CesiumJS | 需要地形、全球范围 3D 或飞行相机运动。 |
切勿提交地图凭证。确认图像、图块和样式的许可和归属。在帧捕获之前,等待图块、图像、字体和相机初始化。从 Remotion 帧时钟驱动相机运动,当可重复性很重要时,保持地理输入本地或内容寻址,并在实际渲染环境中验证无头 Chromium/WebGL。
Studio、渲染和验证
启动 Studio,而不打开外部浏览器:
npx remotion studio --no-open
使用打印的 URL。直接在 /<composition-id> 打开合成,例如 http://localhost:3000/LaunchVideo。
有用的 Studio 标志:
| 参数 | 目的 |
|---|---|
--log=<level> | 设置 error、warn、info 或 verbose。 |
--port=<number> | 请求特定端口。 |
--force-new | 为同一项目和端口启动另一个实例。 |
对于快速的确定性布局检查,渲染一帧:
npx remotion still LaunchVideo out.png --scale=0.25 --frame=30
在 30 fps 下,第 30 帧是一秒。当现有的可视化测试已经提供足够证据时,跳过此项。
在渲染之前,验证 CLI 能看到预期的合成:
npx remotion versions
npx remotion compositions
默认情况下让 Remotion 管理其兼容的浏览器。Prismer 镜像也可能带有 Playwright 浏览器,但其存在不是兼容性保证:完整的 Chrome 二进制文件可能在沙盒崩溃策略下失败,而更新的无头 Shell 可能通过静态渲染但无法完成多帧渲染。仅对镜像拥有的、版本配对的、已通过相邻帧渲染测试的可执行文件传递 --browser-executable。切勿搜索整个文件系统以查找浏览器,也不要将成功启动视为兼容性证据。
仅当用户请求媒体文件时才渲染:
npx remotion render LaunchVideo out.mp4 --codec=h264
npx remotion still LaunchVideo out.png --frame=0
Prismer 沙盒提供 Node 和 FFmpeg,Remotion 在项目缓存中管理兼容的浏览器。优先使用 H.264 视频加 AAC 音频的 MP4 格式以实现广泛播放,除非需要透明度或无损输出。将可写 npm 缓存覆盖范围限定到每个命令:
npm_config_cache=/tmp/npm-cache npx remotion render LaunchVideo out.mp4 --codec=h264
对于 CJK 内容,显式加载并等待固定字体;不要仅依赖系统后备字体系列存在。检查相邻的编码帧以及单独的静态图像:无头渲染器可能生成正确的静态图像,而回退字体在完整渲染期间可能闪烁或丢失字形。
将媒体检查视为交付的仲裁者:
ffprobe -v error \
-show_entries format=duration:stream=codec_name,width,height \
-of compact out.mp4
确认持续时间、尺寸、编解码器和预期的音频流。检查代表性帧,包括动画或文本密集部分中的几个相邻帧,或在可能时播放结果。成功的渲染命令本身不是交付证据。如果孤立的静态图像是正确的,但编码渲染有部分或闪烁的帧,则使用程序化的 renderStill() API,用一个捆绑的服务 URL 和一个浏览器实例来输出经过验证的帧序列,然后使用沙盒 FFmpeg 组装。不要循环 CLI 并为每一帧重新捆绑;报告回退并再次探测组装的文件。
Player、编辑器和渲染服务
使用 Remotion 的 <Player> 进行交互式 React 预览。保持合成可同时用于 Player 和渲染路径;不要分叉视觉逻辑。
按工作负载选择渲染架构:
- 浏览器/客户端渲染用于有限、用户本地的工作。
- 用于受控服务器的 Node worker 和队列。
- Remotion Lambda 用于 AWS 原生弹性渲染。
- 当匹配托管平台和已安装的 Remotion 版本时,使用当前的官方 Vercel 或 Cloudflare 集成。
重要的渲染应该是异步的:返回一个作业 ID,公开进度和失败,持久化存储输出,并使重试具有幂等性。
对于编辑器,显式建模合成属性和时间轴状态。保持渲染输入可序列化和版本化,验证不受信任的属性,限制远程资产访问,并将预览状态与持久项目状态分开。
Remotion 的渲染表面是 React。对于 Vue、Angular 或 Svelte 宿主,使用当前的官方集成,并保持 React 合成边界显式,而不是依赖于记忆化的启动器。
当前文档和升级
优先使用当前的官方文档,而不是记忆化的 API。将 .md 附加到 Remotion 文档 URL 以获取 Markdown,例如:
https://www.remotion.dev/docs/sequence.md
Remotion 文档搜索可以
…
[截断以进行翻译]