remotion-demo-video 是做什麼的?
一個精簡的工作流程,用於建構 Remotion 4.x 宣傳/示範/預告影片。它編碼了專案佈局、動畫原語、渲染命令,以及那些需要花費大量時間重新發現的特定陷阱。
何時使用此技能
當使用者想要製作、編輯、渲染或除錯任何 Remotion 影片時觸發。特別適用於:
- 為產品/論文/儲存庫示引導建立一個新的 Remotion 專案
- 為現有 Remotion 專案新增一個場景
- 將預先錄製的螢幕錄影,透過疊加層、標題、轉場,拼接成一部精美的影片
- 渲染 — 單一場景、完整時間軸、透明(Alpha)疊加層、PNG 序列
- 除錯 Remotion 特有的奇怪問題(composition-id 不匹配、預設值不同步、在
<Sequence>內useCurrentFrame回傳非預期值、Windows 上 ffmpeg 清理時的 EPERM 錯誤等)
如何看待這個工作流程
一部精美的 Remotion 影片不是「一個你可以渲染成單一 MP4 的巨大時間軸」。它是一組小型的、可獨立渲染的場景——每個場景渲染為自己的片段,然後在下游編輯器(DaVinci Resolve、Premiere、Final Cut)中組裝,在那裡你可以加入音樂、旁白和跨場景轉場。
這是從發布 SimWorld 宣傳片中獲得的最重要洞察,而這個技能應該預設引導使用者採用這個工作流程。試圖在 Remotion 內部完成所有事情——最終時間調整、音訊混音、淡入淡出——既痛苦又緩慢;在編輯器中做這些事情則輕而易舉。
讓每個工具發揮所長
- Remotion 擅長於: 程式化、可重複、參數化的場景——動畫標題、資料驅動的圖表、UI 模型、任何你原本會在 After Effects 中手動設定關鍵影格的東西。場景邏輯由程式碼驅動;使用同一個標題搭配 30 種不同的產品名稱」只需要修改一個 prop。
- 下游編輯器擅長於: 時間調整、音訊、淡入淡出、調色、「讓這個場景延長 1.2 秒」。所有這些在編輯器中都是即時可調的,但在 Remotion 中則需要修改程式碼並重新渲染。
工作循環
- 決定腳本。 一個拍點式的綱要,每個拍點附上目標秒數。總長度通常為 30–90 秒。
- 一次設定好專案外殼。
theme.ts(調色盤、字型、fps、尺寸)、Root.tsx(composition 註冊表)、共享元件(Reveal、ProgressBar、easing 函式)。 - 一次建構一個場景。 每個場景都是一個自包含的
<AbsoluteFill>React 元件,以frame 0為設計基礎,並有自己的獨立<Composition>,這樣你就可以單獨渲染它。在 Studio 中迭代(npm run dev)並使用 props 面板;當你滿意時渲染成 MP4(npm run render:<scene>)。 - 將每個完成的場景渲染為自己的片段——這是實際的交付成果,而不是完整時間軸的 MP4。每個場景的片段可以直接拖入任何編輯器的時間軸。對於需要透明度的片段(例如,要疊加在螢幕錄影上的下方三分之一處字幕、Logo),請使用
--codec=prores --pixel-format=yuva444p10le --prores-profile=4444。 - 選擇性地將場景整合到
Main.tsx中,使用theme.ts中TIMELINE常數驅動的<Sequence>區塊。這可以讓你進行完整時間軸的預覽渲染(npm run render)來檢查整體流程——但請將其視為預覽,而非主檔。 - 在 DaVinci / Premiere / Final Cut 中組裝。 依序將每個場景的片段放入時間軸,調整修剪手柄以獲得精確時間,加入音樂/旁白/音效設計,在場景之間套用轉場(對於這種美學風格,1 幀的淡入淡出通常感覺不錯),調色,然後輸出。
何時建議使用完整的 Remotion 渲染
只有在以下情況,完整的時間軸渲染才是正確的輸出:
- 使用者沒有下游編輯器且今天就需要一個單一交付成果。
- 影片完全靜音(完全沒有音樂或旁白)。
- 沒有比硬剪接更複雜的跨場景轉場(即便如此,使用逐場景的方法在程式碼中調整也更容易)。
否則,預設建議使用逐場景渲染加上編輯器組裝。
此技能附帶一個位於 scaffold/ 的可行腳手架——將其複製作為新專案的起點,然後根據需要閱讀下面的參考資料。
實際的端到端循環(當輸入是一篇論文/規格/儲存庫時)
這是此技能真正設計用來處理的工作流程。使用者通常不會走進來說「我想要一個顯示 X 的動畫標題」。他們帶來的是一篇論文 PDF、一個專案規格或一個現有的儲存庫,問題是「把這個變成一部 60 秒的影片」。這個技能的存在就是為了讓這個循環變得快速且易於處理。
1. 原始資料 → 論文 PDF / 規格 / 現有儲存庫 / README
↓
2. Claude 分析 → 提取敘事節奏;找出每個節拍需要
展示的內容(UI 畫面、實驗
錄影、圖表、代理人 POV 等)
↓
3. Claude 搭建腳手架 → 場景程式碼 + 在每個視覺區域中放置
使用佔位符 <VideoPlaceholder slot="..." />,並建立一個
ASSETS.md 列出使用者需要錄製的
所有片段(slot 名稱、時長、應顯示的內容)
↓
4. 使用者錄製素材 → 將 mp4 放入 public/clips/<slot>.mp4
符合腳手架中的 slot 名稱
↓
5. Claude 將 → 將 <VideoPlaceholder> 替換為
佔位符替換為片段 <OffthreadVideo src={staticFile("clips/<slot>.mp4")}>
↓
6. 逐場景迭代 → npm run render:<scene>,觀看,調整文字 /
時間/動畫,重新渲染。程式碼端的
變更很快;如果需要重新錄製素材,
那就是循環中最慢的部分。
↓
7. 在編輯器中組裝 → 將逐場景的 MP4 放入 DaVinci / Premiere / FCP,
加入音樂 + 旁白 + 跨場景
轉場,輸出。
第 2 步詳解 — 分析原始資料
當使用者遞交論文/規格時,在編寫任何程式碼之前執行此操作:
- 提取敘事。 3–5 個節點的敘事弧線是什麼?(「問題 → 我們的方法 → 關鍵結果 → 影響」是一個起點模板;根據實際論文進行調整。)敘事驅動場景列表。
- 對於每個節點,決定要展示什麼。 僅有動畫文字很無聊。每個節點都應該有一個具體的視覺內容——一個 UI 畫面、一個實驗錄影、一個圖表、一個比較、一個系統圖表。將視覺內容與主張匹配起來:如果論文聲稱「方法 X 快 3 倍」,則展示並排的時間比較。
- 區分 Remotion 生成的內容與使用者必須錄製的內容。 Remotion 擅長於:標題、轉場、圖表、UI 模型、佈局、動畫圖表。Remotion 無法生成:使用者產品的真實螢幕錄影、實驗素材、代理人 POV、任何來自專案外部的東西。將這些列為使用者提供的素材。
- 在搭建腳手架之前先草擬時間軸。 逐個節點,附上目標時長,總和為 30–90 秒。更新
theme.ts中的TIMELINE以匹配。應在建立場景之前確定總長度,以便每個場景知道其時間預算。
第 3 步詳解 — 使用佔位符搭建腳手架
此技能的關鍵交接慣例:slot 名稱。使用者需要錄製的每個片段都有一個穩定的 slot 字串。佔位符和實際片段共享這個 slot 名稱。
// 在搭建腳手架期間:
<VideoPlaceholder
slot="agent-loop"
description="連續代理人錄影:行走、失敗、使用更新後的政策重試。"
accent={COLORS.blue}
/>
// 在使用者錄製 public/clips/agent-loop.mp4 之後,切換為:
<OffthreadVideo
src={staticFile("clips/agent-loop.mp4")}
muted
style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }}
/>
始終在腳手架根目錄中撰寫一個 ASSETS.md(腳手架附帶一個模板)。它列出使用者需要填補的每個 slot,包括:slot 名稱(= 檔名)、應顯示的內容、目標時長、建議的長寬比,以及任何時間標記(例如「失敗事件約在 7 秒標記處」)。這是使用者錄製任務的唯一真理來源。
第 4–5 步詳解 — 素材交接
使用者回傳時會將檔案放入 public/clips/ 中。需要檢查三件事:
- 檔名與 slot 名稱完全匹配。
agent-loop.mp4,而不是agent_loop.mp4或AgentLoop.mp4。 - 時長至少與場景長度相同(或者如果較短且旨在重複,則使用
<Loop>)。 - 長寬比合理 — 對於全幀場景,匹配 composition 的 1920×1080。對於 2×2 網格中的卡片,4:3 或 16:9 都可以;保持一致就好。
然後機械式地將每個 <VideoPlaceholder> 替換為 <OffthreadVideo>。每個 slot 只需更換一行;動畫邏輯無需更改。如果使用者的錄影對時間敏感(例如,「失敗事件在 7 秒處」),則將其作為場景 prop 暴露出來,這樣可以調整時間相關的疊加層(FAIL 戳記、獎勵數字)而無需重新錄製。
為什麼這個循環對這個技能很重要
這個技能的工作不是「按需編寫 Remotion 程式碼」——而是讓從原始資料到成品影片的循環變得易於處理。佔位符模式讓 Claude 能夠一次性搭建整個影片的結構,然後交回一份 ASSETS.md,讓使用者確切知道要錄製什麼。沒有它,循環就會停滯——使用者錄錯了東西,或者搭建腳手架需要等待素材,或者程式碼結構必須在事後進行改造。
當使用者帶著原始資料出現時,正確的開場白通常是:
「讓我先閱讀論文/規格。然後我會提出一個逐場景的綱要,附上目標時長、你需要錄製的片段列表,並使用佔位符搭建程式碼腳手架,這樣你就可以在我繼續建立場景的同時開始錄製。」
新專案的快速入門
# 1. 複製腳手架
cp -r <skill-dir>/scaffold ./my-demo
cd my-demo
npm install
# 2. 開啟 Remotion Studio 進行即時編輯
npm run dev # 開啟 http://localhost:3000
# 3. 在迭代時渲染單一場景
npm run render:opening
# 4. 準備好時渲染完整時間軸
npm run render
腳手架包含一個可運作的場景(OpeningTitle)、暖奶油色主題、Reveal + easing 函式、一個 ProgressBar、一個佔位用的 Main 時間軸,以及一個已預先配置好渲染腳本的 package.json。
專案佈局(有效的形狀)
my-demo/
├── package.json # 渲染腳本、預渲染鉤子
├── remotion.config.ts # codec / 像素格式預設值
├── tsconfig.json
├── public/
│ ├── assets/ # 標誌、靜態圖片 (staticFile("assets/..."))
│ └── clips/ # 螢幕錄影 / 影片來源
├── scripts/
│ └── sync-clips.mjs # 在每次渲染前正規化片段
└── src/
├── index.ts # registerRoot(Root)
├── Root.tsx # <Composition> 註冊表 — id, schema, defaultProps
├── theme.ts # COLORS, FONT_STACK, RADIUS, SHADOWS, TIMELINE
├── fonts.ts # @remotion/google-fonts + 本機 @font-face
├── Main.tsx # 完整時間軸組合器 (<Sequence> 區塊)
├── components/
│ ├── Reveal.tsx # 淡入 + 上移包裝器 (delay/duration/rise)
│ ├── Easing.ts # EASE_OUT, EASE_IN_OUT (貝茲曲線)
│ ├── ProgressBar.tsx # 固定於底部的 4 階段指示器
│ └── VideoPlaceholder.tsx # 帶有 slot 標籤的灰色卡片,之後切換為 OffthreadVideo
└── scenes/
├── OpeningTitle.tsx # 每個檔案一個場景,每個都是一個 <AbsoluteFill>
├── ProductReveal.tsx
├── AgentLoop.tsx
└── ...
為什麼是這種形狀:場景是獨立的(易於單獨渲染和迭代),共享的視覺語言存在於 theme.ts + components/ 中,以保持外觀一致,而 Root.tsx 是關於什麼可以渲染的唯一真理來源。
Composition 模式(仔細閱讀)
每個場景導出三樣東西:
- 一個 zod schema — 定義 Remotion Studio 中的 props 面板 UI(滑桿、下拉選單)。
- 一個
*_DEFAULTS常數 — 當此場景嵌入完整時間軸時,由Main.tsx使用的值。 - React 元件本身,類型為
React.FC<Props>。
// src/scenes/ProductReveal.tsx
import { z } from "zod";
export const productRevealSchema = z.object({
titleSize: z.number().min(40).max(280),
subtitleSize: z.number().min(14).max(80),
outerPaddingV: z.number().min(0).max(200),
});
export type ProductRevealProps = z.infer<typeof productRevealSchema>;
export const PRODUCT_REVEAL_DEFAULTS: ProductRevealProps = {
titleSize: 168,
subtitleSize: 64,
outerPaddingV: 80,
};
export const ProductReveal: React.FC<ProductRevealProps> = (p) => { ... };
然後在 Root.tsx 中,使用一份內聯的預設值拷貝來註冊它:
<Composition
id="ProductReveal"
component={ProductReveal}
durationInFrames={120}
fps={30}
width={1920}
height={1080}
schema={productRevealSchema}
defaultProps={{
// ⚠ PRODUCT_REVEAL_DEFAULTS 的內聯拷貝 — Remotion Studio 的
// 「儲存預設 props」會透過 AST 將資料寫回此物件。請保持
// 與上述導出的常數同步。
titleSize: 168,
subtitleSize: 64,
outerPaddingV: 80,
}}
/>
為什麼要重複: Remotion Studio 的「儲存預設 props」按鈕會透過 AST 操作來編輯 Root.tsx 中的 defaultProps={…} 字面值。它無法改寫一個匯入的常數。因此,內聯拷貝是 Studio 會修改的那個,而導出的 *_DEFAULTS 則是 Main.tsx 會讀取的那個。當 Studio 寫入新值時,將它們複製到導出的常數中,以便完整時間軸的渲染也能使用它們。這是 references/pitfalls.md 中的陷阱 #4。
動畫原語(你實際需要的小集合)
Remotion 中的所有動畫都是 useCurrentFrame() + interpolate()。沒有內部時鐘,沒有「播放」——每一幀都是 frame 的純函數。
你需要的……