這是撰寫 Remotion React Markup 的指導方針。 如果這不相關,請改為載入 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'})。其內部標記可以回傳一個片段;包裝器會處理佈局和實例樣式。較小的元素(如下方三分之一)可以保留預設佈局,並將 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}),
}),
}}
// 👎 非內聯的值和轉換字串在 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。
使用 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",
}}
/>
</>
);
};
延遲與修剪
將計時直接放在支援它的元件上。有關支援的屬性、範例和操作順序,請參閱計時屬性。
地圖
如果希望在影片中包含地圖,請參閱 Remotion 地圖。
文字高亮與註解
有關文字高亮(高亮標記)、圓圈、底線、刪除線、劃掉文字、方框等,請參閱 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> 註冊相同的匯出元件引用。直接使用其計時屬性渲染互動式元件,或者當該包裝器有特定用途時,將其作為序列的唯一子元件渲染。如果選定的節點已經是一個序列,則保留其屬性並提取其子元件。註冊需要與其父元件使用方式等效的尺寸、影格率、持續時間和 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