Remotion 動態圖像 — 以影格為基礎的 2D 影片
Remotion 將 React 渲染為影片。每個影格都是影格索引的純函數(pure function),因此輸出具有確定性、可比較且可審查。它是文字主導和數據驅動影片的最佳預設選擇,也是將 Blender 或 Manim 的輸出與字體排印、字幕和轉場效果組合在一起的最佳「合成器」。
請閱讀 references/REMOTION-PLAYBOOK.md 了解慣例,以及 references/REMOTION-RECIPES.md 取得可直接複製的元件。請查閱官方文件和已安裝的 CLI 以了解當前 API:https://www.remotion.dev/docs/
1. 設定
node --version # 需要 18+,建議使用 20/22 LTS
npx create-video@latest my-video # 選擇空白範本
cd my-video
npx skills add remotion-dev/skills # 官方 Remotion 代理技能(建議)
npm run dev # 在 http://localhost:3000 啟動 Remotion Studio
渲染:
npx remotion render <entry> <CompositionId> out/video.mp4 --codec=h264
npx remotion render <entry> <CompositionId> out/preview.mp4 --scale=0.5
npx remotion render <entry> <CompositionId> out/frames --sequence --image-format=png
確切標誌因版本而異;請使用 npx remotion render --help 確認。
授權:Remotion 是原始碼可取得(source-available),而非 OSI 定義的開源軟體。根據其當前條款,個人和符合條件的小型組織可免費使用;較大型或商業/自動化用途可能需要付費授權。在承諾交付成果之前,請查閱 https://www.remotion.dev/docs/license/pricing 以了解使用者的實際情況。請勿將其描述為「MIT」或「任何公司均可免費使用」。
2. 專案結構
請適應初始範本而非與其對抗:
src/
Root.tsx # 註冊 composition
compositions/
MainVideo.tsx # 將場景依序串接
scenes/
Intro.tsx Feature.tsx Outro.tsx
components/
AnimatedTitle.tsx Background.tsx Grid.tsx Caption.tsx Transition.tsx
animations/
easing.ts timing.ts reveal.ts
data/
content.ts # 文案和數字,與版面分離
styles/
tokens.ts # 色板、字級、間距
public/
audio/ fonts/ images/ video/
規則:
- 每個場景一個檔案;
MainVideo僅負責串接它們。 - 文案和數據存放在
data/,切勿內聯分散於五個場景中。 - 設計 token 存放在
styles/tokens.ts,不要隨處散落魔術十六進位色值。 - 可重複使用的基礎元件放在
components/,切勿在每個場景中重複。 - 所有
public/下的資源皆使用staticFile("...")。
3. 確定性(硬性規則)
所有視覺狀態皆衍生自當前影格:
import { useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
const frame = useCurrentFrame();
const { fps, durationInFrames, width, height } = useVideoConfig();
禁止用作動畫驅動:Date.now()、setTimeout、setInterval、requestAnimationFrame、CSS 關鍵影格(keyframes)、未設定種子的 Math.random()、以及在渲染之間變化的非同步數據。
當需要變化時使用帶種子的隨機性:
export const rand = (seed: number) => {
const x = Math.sin(seed * 12.9898) * 43758.5453;
return x - Math.floor(x);
};
非同步資源必須在影格渲染前解析完成。請正確使用 delayRender/continueRender,務必在 catch 中執行 continueRender,或者建議在影格外部預先載入資源。
4. 時間軸
durationInFrames是明確的。在 30 FPS 下,10 秒 = 300 影格,索引 0–299。- 統一換算:
const s = (n: number) => Math.round(n * fps)。 - 場景邊界來自累積時間線,切勿對每個場景單獨四捨五入。
- 使用
<Sequence from={start} durationInFrames={len}>;檢查沒有任何元素超出 composition 的範圍。 - 讓重疊是刻意安排的;一個影格的間隙會造成明顯閃爍。
- 當持續時間或尺寸來自 props 時,請使用
calculateMetadata。
5. 動畫工藝
| 動作 | 原始方法 |
|---|---|
| 入場/穩定 | spring 或搭配 Easing.out(Easing.cubic) 的 interpolate |
| 出場 | Easing.in(Easing.cubic) |
| 對稱移動 | Easing.inOut(Easing.cubic) |
| 機械式/連續 | Easing.linear |
| 數字/顏色/旋轉 | 使用帶有 clamp 外插法的 interpolate |
| 錯開 | 為每個元素偏移 from(30–80 毫秒) |
const enter = spring({ frame, fps, config: { damping: 18, stiffness: 120, mass: 0.8 } });
const opacity = interpolate(frame, [0, 12], [0, 1], {
extrapolateLeft: "clamp", extrapolateRight: "clamp",
});
不要對所有東西都用 spring。不要為每個屬性都做動畫。偏好使用變形(transform)和不透明度(opacity)(GPU 友善)而非版面屬性。確保短標題至少有約 0.8–1.2 秒的停留時間,內文則需要更長。
6. 字體排印與設計
- 設定字級(例如在 1080p 下使用 96/64/40/28/20 px)並統一使用。
- 從
public/fonts使用@remotion/fonts或初始範本中記載的模式載入字型,並在渲染前等待其就緒。避免依賴系統字型。 - 檢查行長(約 45–75 個字元)、行高(顯示字型約 1.1–1.4)以及字距。
- 保留 5–8% 的安全邊距;針對垂直/社群格式檢查平台 UI 覆蓋層。
- 針對波斯語/阿拉伯語:正確設定
dir="rtl"、使用支援字型塑形的字型、驗證混合數字/URL,並檢查渲染後的影格——無頭 Chrome 對文字的塑形方式與某些設計工具不同。 - 逐字元驗證最終文案。影片中的錯字是出貨缺陷。
7. 構圖與轉場
- 每個節拍只有一個焦點;引導觀看者的視線,而非讓整個畫面都在動。
- 使用轉場詞彙:硬切、6–10 影格的淡入淡出、遮罩擦拭、縮放穿越、模糊穿越。刻意重複使用。
- 更傾向於在節拍上使用硬切,而非對所有東西都用溶解。
- 對於長影片,首先建立時間表(秒 → 影格 → 場景)並將其編碼在一個檔案中。
8. 音訊與字幕
import { Audio, staticFile } from "remotion";
<Audio src={staticFile("audio/music.mp3")} volume={0.6} />
- 僅在聲音應該開始的序列中匯入。
- 使用
interpolate對volume進行淡入/淡出;避免點擊聲。 - 在語音旁白下降低音樂音量;確保對話清晰可聞。
- 字幕:針對社群平台使用
<Caption>進行燒錄,或針對其他平台匯出.srt。注意安全區域和閱讀速度(約每秒 15–20 個字元)。 - 務必在編碼後的檔案上驗證混音,而非僅在預覽中。
9. 效能
- 在記憶體受限的機器上減少並行數:
--concurrency=4。 - 避免巨大的模糊效果、濾鏡鏈、大型 SVG 和數千個 DOM 節點;對於密集圖形請使用 Canvas。
- 快取靜態背景(使用
<Freeze>或預渲染的圖片)。 - 最佳化圖片(正確尺寸、在支援時使用 WebP/AVIF)並修剪影片資源。
- 使用
--scale=0.5並先渲染短影格範圍來進行預覽渲染。
10. 批次與數據驅動影片
- 定義 props 架構並在渲染前進行驗證。
- 將數據保留在 JSON/CSV 中;將版面保留在元件中。
- 文字適配:最大行數、後備字型大小、截斷規則、溢出測試。
- 使用不同的
--props從同一個 composition 渲染每個變體。 - 撰寫一個清單並驗證每個輸出的持續時間和非空大小。
npx remotion render src/index.ts Main out/acme.mp4 --props='{"brand":"Acme","tagline":"Build faster"}'
11. 與 Blender 混合使用
- 以與 Remotion composition 相同的 FPS、解析度和影格數渲染 Blender 鏡頭。
- 除非專案定義了偏移量,否則將 Blender 影格 1 映射到 Remotion 影格索引 0。
- 若要透明背景,請匯出 PNG 序列或 ProRes 4444,並在棋盤格背景上驗證 Alpha 通道;MP4 沒有 Alpha 通道。
- 匹配色彩管線:以與瀏覽器輸出相同的傳輸/視圖轉換假設來編碼 Blender 的輸出,然後檢查合成結果。
- 使用
<OffthreadVideo>(而非<Video>)將輸出放置在<Sequence>內,並根據影格映射設定startFrom/影格偏移量。
12. 交付前的驗證
npx remotion compositions src/index.ts # 列出 ID 和尺寸
ffprobe -v error -show_entries format=duration,size \
-show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt,channels \
-of json out/video.mp4
然後檢查影格 0、每個場景邊界、每個轉場、每個文字密集的影格以及最後一個影格。檢查是否有文字被裁切、缺少字型、黑格和音訊偏移。請參閱 video-composition 了解編碼/正規化的食譜,以及 motion-studio/references/QUALITY-CHECKLIST.md 了解完整的檢查清單。
13. 常見失敗模式
- 找不到 Composition → 入口檔案或 ID 錯誤;執行
npx remotion compositions。 - 空白渲染但預覽正常 → 資源路徑在 Studio 中可解析但在無頭模式下不行;使用
staticFile,檢查public/的大小寫。 - 字型回退到 Times → 字型在渲染前未載入;等待就緒。
- 每次渲染輸出不同 → 使用了真實時間或未設定種子的隨機性。
- 記憶體耗盡或極慢 → 降低
--concurrency、減少模糊/比例、分割渲染。 - 音訊不同步 →
startFrom錯誤、FPS 不匹配或來源影片為可變影格率。