此文档提供了编写 Remotion React 标记的指导。 如果不相关,请加载 Remotion 最佳实践。
保留用户更改
用户可能会在对话之外对代码进行编辑。
如果在编辑过程中检测到意外的更改,请不要覆盖它,假定更改是故意的,或者请求确认。
通用规则
在合成(Composition)或场景(Scene)组件内部,请使用 useVideoConfig() 获取 fps 用于所有秒到帧的计算,包括 timing props 如 from、trimBefore、durationInFrames 和 premountFor。将表达式保留在 JSX 节点上,例如 trimBefore={4 * fps},这样可以让 Studio 编辑时间轴,并确保在合成帧率更改时保持正确。
使用 useCurrentFrame() 和 interpolate() 驱动动画。
CSS transition 或 animation 无法正确渲染,需要重构。
Tailwind 动画类无法正确渲染,需要重构。
使用 Easing.bezier() 和 Easing.spring() 自定义时间曲线。
根据 Remotion 交互最佳实践构建您的标记。
对于具有可编辑属性的场景,优先使用 Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'})。其内部标记可以返回一个片段(fragment);包装器处理布局和实例样式。对于像下三分之一(lower third)这样的有大小元素,可以保留默认布局并将 style 转发到其自身根节点。将可复用的场景注册为连接合成(connected compositions)。
将 timing 直接放在支持它的组件上;避免使用多余的 <Sequence> 包装器。
为每个支持 premountFor 的定时组件分配一秒钟的预挂载时间:
premountFor={fps},其中 fps 来自 useVideoConfig()。将此应用于媒体、交互组件、<Sequence>、<TransitionSeries.Sequence> 和 <TransitionSeries.Overlay>,包括嵌套在场景内部的定时组件。当嵌套项目需要在父级开始前挂载时,也要预挂载父时间线项目。一个没有 premountFor 的组件,例如 <TransitionSeries.Transition>,无需替代。
Studio 编辑创建项目的 JSX 源节点。将每个合成注册、片段、场景、图层和序列作为其独立的 JSX 节点进行编写,并将其可编辑属性内联放置。 当生成的实例被作为单个源模板有意控制时,程序化循环是合适的;当用户需要单独编辑这些实例时,则不适用。
import { useCurrentFrame, useVideoConfig, Easing, interpolate, Interactive } from "remotion";
export const FadeIn = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
return (
<Interactive.Div
name="Title"
premountFor={fps}
style={{
opacity: interpolate(frame, [0, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
}),
}}
>
Hello World!
</Interactive.Div>
);
};
将 interpolate() 调用内联在 style prop 中。
优先使用 scale,translate,rotate CSS 属性而非 transform。
// 👍 内联可编辑关键帧和变换简写
style={{
scale: interpolate(frame, [0, 100], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.spring({damping: 200}),
output: 'perceptual-scale' // 对于 `scale` 动画,使用 "output: 'perceptual-scale'"
}),
translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.spring({damping: 200}),
}),
rotate: interpolate(frame, [0, 100], ["20deg", "90deg"], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.spring({damping: 200}),
}),
}}
// 👎 非内联值和变换字符串在 Studio 中更难编辑
const scale = interpolate(frame, [0, 100], [0, 1]);
style={{
transform: `scale(${scale})`,
}}
资源
将资源放置在项目根目录的 public/ 文件夹中。
使用 staticFile() 引用 public/ 文件夹中的文件。
媒体组件
使用 @remotion/media 包中的 <Video> 和 <Audio> 添加视频和音频。
使用 <CanvasImage> 组件添加图片。
对于动画 GIF、APNG、WebP 或 AVIF 图片,使用 <AnimatedImage> 组件;如果不使用 Chrome,则使用 @remotion/gif。
使用 staticFile() 处理 public/ 中的文件,或直接传入远程 URL:
import { Audio, Video } from "@remotion/media";
import { staticFile, CanvasImage, AnimatedImage, useVideoConfig } from "remotion";
export const MyComposition = () => {
const {fps} = useVideoConfig();
return (
<>
<Video src={staticFile("video.mp4")} premountFor={fps} style={{ opacity: 0.5 }} />
<Audio src={staticFile("audio.mp3")} premountFor={fps} />
<CanvasImage
src={staticFile("logo.png")}
premountFor={fps}
style={{ width: 100, height: 100 }}
/>
<Video src="https://remotion.media/video.mp4" premountFor={fps} />
<AnimatedImage src={staticFile('nyancat.gif')} premountFor={fps} />
</>
);
};
如果合成主要是视频或音频片段的时间线,在选择其源结构之前,请阅读 video-editing.md。
示例场景
一段带有下三分之一文字条的背景视频。
下三分之一是一个具有自身时间线的交互组件,注册为一个连接合成。
其文本通过 children 传递,accentColor 是一个可编辑属性。
淡入效果在调用处内联地设置关键帧。
// MyScene.tsx
import { Video } from "@remotion/media";
import { Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
import { LowerThird } from "./LowerThird";
export const MyScene = () => {
const { fps } = useVideoConfig();
const frame = useCurrentFrame();
return (
<>
<Video
name="Background"
src="https://remotion.media/video.mp4"
premountFor={fps}
objectFit="cover"
style={{
position: "absolute",
width: "100%",
height: "100%",
}}
/>
<LowerThird
name="Lower third"
from={1 * fps}
premountFor={fps}
accentColor="#0b84f3"
style={{
opacity: interpolate(frame, [1 * fps, 2 * fps], [0, 1], {
extrapolateRight: "clamp",
extrapolateLeft: "clamp",
easing: Easing.bezier(0.16, 1, 0.3, 1),
}),
}}
>
Jane Doe, Product Designer
</LowerThird>
</>
);
};
// LowerThird.tsx
import type React from "react";
import { Interactive, type InteractivitySchema } from "remotion";
type LowerThirdProps = {
readonly children: React.ReactNode;
readonly accentColor: string;
readonly style?: React.CSSProperties;
};
const LowerThirdInner: React.FC<LowerThirdProps> = ({
children,
accentColor,
style,
}) => {
return (
<Interactive.Div
style={{
position: "absolute",
left: 80,
bottom: 80,
display: "flex",
alignItems: "center",
gap: 20,
backgroundColor: "white",
borderRadius: 16,
padding: "20px 32px",
color: "black",
fontFamily: "Helvetica, Arial, sans-serif",
fontSize: 48,
fontWeight: 600,
...style,
}}
>
<div
style={{
width: 8,
alignSelf: "stretch",
borderRadius: 4,
backgroundColor: accentColor,
}}
/>
{children}
</Interactive.Div>
);
};
const lowerThirdSchema = {
...Interactive.childrenSchema,
accentColor: {
type: "color",
default: "#0b84f3",
description: "强调色",
},
} as const satisfies InteractivitySchema;
export const LowerThird = Interactive.withSchema({
Component: LowerThirdInner,
componentName: "LowerThird",
schema: lowerThirdSchema,
wrapInSequence: true,
});
import { Composition } from "remotion";
import { LowerThird } from "./LowerThird";
import { MyScene } from "./MyScene";
export const RemotionRoot: React.FC = () => {
return (
<>
<Composition
id="MyScene"
component={MyScene}
durationInFrames={60}
fps={30}
width={1280}
height={720}
/>
<Composition
id="LowerThird"
component={LowerThird}
durationInFrames={30}
fps={30}
width={1280}
height={720}
defaultProps={{
children: "Jane Doe, Product Designer",
accentColor: "#0b84f3",
}}
/>
</>
);
};
延迟与裁剪
将 timing 直接放在支持它的组件上。有关支持的 props、示例和操作顺序,请参见 Timing props。
地图
如果需要在视频中包含地图,请参见 Remotion Maps。
文本高亮与注释
关于文本高亮(高亮标记)、圆圈、下划线、删除线、划掉文本、方框等,请参见 text-highlights.md。
多场景视频
如果计划制作包含多个连续场景的视频,请参见 multi-scene-video.md。
连接合成
当一个场景或图层组需要其自己的可编辑时间线时,请遵循 connected-compositions.md。在多场景视频中,对于重要的场景,优先使用这种结构。
预合成操作
对于 Studio 的请求,例如 Pre-compose Ambient glow (src/BarChart.tsx:134),在指定位置找到选定的序列标记。制作一个连接合成,遵循 connected-compositions.md:将标记提取到一个命名组件中,最好使用 Interactive.withSchema({wrapInSequence: true}) 使其可交互,并在根目录中使用唯一的 <Composition> 注册相同的导出组件引用。直接使用其 timing props 渲染交互组件,或者当包装器有特定用途时,将其作为序列的唯一子元素渲染。如果选定的节点已经是一个序列,保留其属性并提取其子元素。注册时需要包含尺寸、fps、持续时间和等同于其父级使用的 defaultProps。不注册合成的组件提取不能完成预合成请求。
将继承的样式(如 fontFamily 和字体加载)带入提取的组件中;参见 父级独立性。
画外音
关于使用 ElevenLabs TTS 为 Remotion 合成添加 AI 生成的画外音,请参见 voiceover.md。
嵌入视频
关于嵌入视频的高级知识——裁剪、音量、速度、循环、音调,请参见 embedding-videos.md。
嵌入音频
关于高级音频功能,如裁剪、音量、速度、音调,请参见 audio.md。
裁剪
如果需要裁剪组件的可见矩形,请参见 cropping.md。
转场
关于场景转场模式,请参见 transitions.md。
运动模糊
当添加运动模糊或运动轨迹时,请阅读 motion-blur.md 以了解首选的 HTML-in-canvas 方法、预览要求以及替代方案。
视觉与像素效果
当添加视觉效果时,首先确定它应该影响哪个元素或组。
如果目标组件支持 effects 并且下面的内置效果符合要求,优先使用该效果而不是 CSS 近似。例如,当暗角效果应该影响某个 <Video> 时,直接将其 vignette() 应用到 <Video> 的 effects prop 上。
以下组件直接接受 effects:
| 导入来源 | 组件 |
|---|---|
remotion | <Img>、<CanvasImage>、<AnimatedImage>、<Solid>、<HtmlInCanvas> |
@remotion/media | <Video> |
@remotion/gif | <Gif> |
@remotion/rive | <RemotionRiveCanvas> |
@remotion/shapes | <Arrow>、<Callout>、<Circle>、<Ellipse>、<Heart>、<Pie>、<Polygon>、<Rect>、<Spark>、<Star>、<Triangle> |
普通的 HTML 元素、<Interactive.*> DOM 包装器、<AbsoluteFill> 和 <Sequence> 不直接接受 effects。要将效果应用于它们的内容,请将它们包装在 <HtmlInCanvas> 中,并将 effects prop 放在包装器上。
对于其他元素,在合适的情况下优先使用常规的 HTML 和 CSS。当需要对整个组进行后处理且环境支持时,考虑将一组元素包装在 <HtmlInCanvas> 中。
在应用效果之前,请阅读 effects.md 了解导入、参数和设置。它还包括当没有匹配的内置效果时,使用 createEffect() 创建可重用的自定义效果。
可用效果
来自 @remotion/effects 的内置效果:
- 颜色和色调:
brightness()、colorCorrection()、colorKey()、contrast()、duotone()、exposure()、grayscale()、hue()、invert()、levels()、linearGradient()、linearGradientTint()、lut()、saturation()、shadowsHighlights()、thermalVision()、tint()、vibrance()、whiteBalance()。 - 模糊和轨迹:
blur()、lightTrail()、linearProgressiveBlur()、radialProgressiveBlur()、regionBlur()。 - 失真和变形:
bulge()、colorWander()、cube()、displacement()、glitch()、hexagonalPixelate()、mirror()。 - 覆盖层和艺术效果:
layers()、noise()、overlay()、pixelate()、sketch()。 - 模糊和发光:
glow()、dropShadow()、chromeKey()、chromaKey()。
有关可用的效果、参数和视觉预览的完整列表,请参见 effects.md。