Product video in Remotion
一個無臉的產品示範(格式 3 的短影片格式)以程式碼建構。語音驅動時間軸:每句 對白被朗讀,鏡頭片段依序銜接,影片的場景和字幕跟隨這些時間戳記。UI 以 React 從產品 自身的樣式重建,因此在任何縮放下都清晰銳利,並在產品或腳本變更時可重新渲染。除了渲染好的 MP4 之外,所有內容都存放在 git 中,因此任何舊版皆可透過簽出分支並重新渲染取得。
輸入項目:Node 22、ffmpeg、TTS(草稿使用 edge-tts;發布前使用授權語音)、產品的 CSS
token 與標記(登陸頁面的產品示範通常是最佳來源),以及腳本。
工作流程
1. 設定
npx create-video@latest --blank reel && cd reel && npm i @remotion/google-fonts
保留一個 theme.ts,包含畫面尺寸(1080×1920,30 fps)、佈局區域以及從其 CSS 複製的產品色彩 token。
我們的區域:標題條居中於 y=118,白色 UI 區域從 y=220 開始,高度 1250 px(佔高度 65%,全寬),
字幕居中於 y=1640。
2. 將腳本寫為資料
script.json,每個字幕行一個條目;*星號* 標記強調的詞:
{ "voice": { "name": "en-US-AndrewMultilingualNeural", "rate": "+10%" }, "gap": 0.16,
"lines": [
{ "scene": "wall", "text": "I stopped *prompting* Claude Code." },
{ "scene": "wall", "text": "I *hired* it.", "pause": 0.5 },
{ "scene": "end", "text": "Hire your first *AI employee*.", "say": "Crewmark. Hire your first AI employee." }
],
"post": { "tiktok": { "caption": "…", "hashtags": [] }, "instagram": { "caption": "…", "hashtags": [] } } }
say 用於當語音與字幕不同時;pause 讓場景在該行後停留更久。
3. 語音 → 時間軸
一個小型 Node 腳本將每行朗讀為各自的 WAV 檔案,去除靜音,以 gap/pause 之間的間隔
將它們依序銜接,合成為 public/voice.wav(adelay + amix,然後使用 loudnorm=I=-14 以達到社群音量標準),
並寫入 public/timeline.json,其中包含每行的 from/to(以影格為單位):
{ "durationInFrames": 959, "lines": [ { "scene": "wall", "text": "I stopped *prompting* Claude Code.", "from": 12, "to": 51 } ] }
根據文字和語音快取每個錄音,這樣編輯一行就不會重新朗讀其餘部分。組合的
durationInFrames 來自此檔案。
4. 場景跟隨時間軸
將具有相同 scene 的連續行分組到一個 <Sequence> 中;為每個場景提供其行偏移量(「節拍」),使
動畫能對應到詞語。字幕是一個讀取當前行的元件。
關鍵元件是在 UI 區域上方的鏡頭:以真實 CSS 像素佈局 UI,然後縮放和平移。
export function Cam({ zoom, x = 0, y = 0, children }: { zoom: number; x?: number; y?: number; children: React.ReactNode }) {
return (
<div style={{ position: "absolute", inset: 0, overflow: "hidden", background: "#fff" }}>
<div style={{ position: "absolute", width: 1080 / zoom + x, height: STAGE_H / zoom + y,
transform: `scale(${zoom}) translate(${-x}px, ${-y}px)`, transformOrigin: "0 0" }}>{children}</div>
</div>
);
}
當 zoom = 1080 / 520 時,一個 520 像素寬的佈局(兩個 248 像素的卡片)剛好填滿寬度。使用緩動插值在
節拍之間對 zoom、x 和 y 進行動畫處理,以推近到正在點擊的按鈕。
效果不錯的場景想法:卡片市場、自動填寫的表單(附有插入符的輸入文字)、卡片在不同欄間移動的面板、 活動串流、自我繪製的圖表(標記為「示範資料」)、AI 提問而您批准的聊天畫面、以登陸頁面英雄風格呈現的 結尾卡片。
5. 渲染並檢視
node voice.mjs && npx tsc --noEmit
npx remotion still src/index.ts Reel /tmp/f0.png --frame=0 # 封面
npx remotion render src/index.ts Reel out/reel.mp4
ffmpeg -v error -y -i out/reel.mp4 -vf fps=1,scale=200:-1,tile=8x5 -frames:v 1 out/sheet.jpg
檢視格點圖以及每個場景的靜態畫面:UI 區域完整、區域底部沒有被裁切、字幕位於底部區域內、 每行對應正確的場景、第 0 影格不為空白。
6. 版本控制
提交原始碼、腳本以及 voice.wav/timeline.json;將 out/ 和下載檔案保留在 .gitignore 中。要恢復
舊版剪輯,請簽出對應的提交並重新渲染。
檢查清單
- 顏色、字型和文案取自產品自身的 CSS 和文字
- UI 區域高度 ≥ 50%,全寬;僅上方和下方有條狀區域
- 第 0 影格已渲染並檢查是否可作為封面
- 每個場景都展示了產品實際擁有的功能
- 示範數字已標示
- 每次渲染後檢視格點圖
- 原始碼和語音已提交;MP4 不提交
我們遇到的陷阱
- 第一個場景的淡入效果導致第 0 影格(封面)為空白。請跳過第一個場景的進入動畫。
- **內容被鏡頭裁切。**為某個縮放級別設計的圖表在場景的縮放下超出了區域底部。請在每個鏡頭移動的 結束時檢查靜態畫面,而不僅僅是開始時。
- **下半部過於稀疏。**在低縮放下,短場景會讓白色區域底部留空。請進一步縮放,或將某些元素 錨定到底部(訊息輸入框、頁尾),使區域看起來像真實的螢幕。
- **需要 .srt 的審查腳本。**我們舊版 ffmpeg 管線的審查工具讀取的字幕時間戳記是 Remotion 不會產生的。
使用帶有
fps=1,tile的格點圖可以達到相同效果。 - 在一個樣式物件中重複的
transform(例如按壓動畫加上進入動畫)會導致類型檢查失敗。 請包裹元素,並將每個 transform 放在不同的層上。 - **正式版本使用 Edge TTS。**它免費且適合草稿,但與任何真人語音相比顯得很機械,而且 沒有商業授權。在大規模發布前,請替換 TTS 函式。
相關技能
- short-video-formats:佈局規則以及應建構哪種格式。
- social-publish:
script.json中保留的發布文案。 - video-retro:下一次渲染應變更的內容。