这是编写 Remotion React 标记的指南。 如果与此无关,请加载 Remotion 最佳实践。
保留用户更改
用户可能在对话之外对代码进行编辑。
如果您发现中间发生了意外的更改,请不要覆盖它,假设它是有意为之或请求确认。
通用规则
在合成或场景组件内部,使用 useVideoConfig() 获取 fps,用于所有秒到帧的计算,包括 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'})。它们内部的标记可以返回一个片段;包装器处理布局和实例样式。像 lower third 这样尺寸固定的元素可以保留默认布局,并将 style 转发给它们自己的根元素。将可重用的场景注册为连接合成。
将时间属性直接放在支持它的组件上;避免冗余的 <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 属性中。
优先使用 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}),
}),
}}
// 👎 非内联值和 transform 字符串在 Studio 中变得更难编辑
const scale = interpolate(frame, [0, 100], [0, 1]);
style={{
transform: `scale(${scale})`,
}}
资产
将资产放置在项目根目录的 public/ 文件夹中。
使用 staticFile() 引用 public/ 文件夹中的文件。
媒体组件
使用 @remotion/media 中的 <Video> 和 <Audio> 添加视频和音频。
使用 <CanvasImage> 组件添加图像。
使用 <AnimatedImage> 添加动画 GIF、APNG、WebP 或 AVIF 图像,如果不是使用 Chrome,请使用 @remotion/gif。
对 public/ 中的文件使用 staticFile(),或直接传递远程 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。
示例场景
一个带有 lower third 的背景视频。
lower third 是一个带有自己时间轴的交互式组件,注册为连接合成。
它的文本作为 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: "Accent color",
},
} 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",
}}
/>
</>
);
};
延迟与修剪
将时间属性直接放在支持它的组件上。有关支持的属性、示例和操作顺序,请参阅时间属性。
地图
如果希望在视频中包含地图,请参阅 Remotion 地图。
文本高亮与注释
有关文本高亮(高亮标记)、圆圈、下划线、删除线、划掉文本、方框,请参阅 text-highlights.md。
多场景视频
如果计划制作包含多个后续场景的视频,请参阅 multi-scene-video.md。
连接合成
当一个场景或一组图层值得拥有自己的可编辑时间轴时,请遵循 connected-compositions.md。在多场景视频中,对于重要的场景,优先使用这种结构。
预合成操作
对于诸如 Pre-compose Ambient glow (src/BarChart.tsx:134) 这样的 Studio 请求,在给定位置找到选定的序列标记。创建一个连接合成,遵循 connected-compositions.md:将标记提取到一个命名组件中,最好使用 Interactive.withSchema({wrapInSequence: true}) 使其具有交互性,并在根目录中使用唯一的 <Composition> 注册相同的导出组件引用。直接使用其时间属性渲染交互式组件,或者当该包装器有特定用途时,将其作为序列的唯一子元素渲染。如果选定的节点已经是一个序列,则保留其属性并提取其子元素。注册需要尺寸、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 近似值。例如,当晕影应该影响该视频时,可以直接将 vignette() 应用于 <Video> 的 effects 属性。
这些组件直接接受 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 属性放在包装器上。
对于其他元素,在合适的情况下优先使用常规 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(),sceneBlur()。 - 位移和变形:
displacement()。 - 像素化:
pixelate(),edgeWork()。 - 样条曲线和遮罩:用
spline()函数绘制贝塞尔形状,用mask()通过亮度或其他通道遮罩内容。 - 合成和混合:
composite(),useMerge(),multiplyBlend(),screenBlend(),overlayBlend()。 - 眩光:
lensGlare(),anamorphicLensFlare()。 - 特殊效果:
chromaKey(),customMatrix(),dropShadow(),glitch(),noise(),pageCurl(),polaroid(),scanlines()等。
当没有完全匹配时,首先检查 effects.md 的目录,看看是否需要。如果没有任何效果符合目的,可以使用 CSS 效果对普通元素进行近似处理,或者将 HTML-in-canvas 与来自 @remotion/effects 的效果结合使用。不要手动实现 chromaKey() 或类似的内置效果。如果 @remotion/effects 中没有合适的效果,请参考 @remotion/lottie、@remotion/three、@remotion/renderers 或 @remotion/shapes。
管理效果列表
请参阅 effects-manager.md 以获取介绍。