動態圖形
透過撰寫渲染器逐幀搜尋的網頁來生成影片。 以下兩種方式做的是同一件事;差別在於你撰寫的內容。
- HyperFrames — 一個 HTML 檔案,CSS 負責外觀,一個暫停的 GSAP 時間軸負責
動畫。
node scripts/hf.mjs會執行固定版本並從已提交的鎖定檔案安裝一次。 Apache 2.0。 - Remotion — React 元件。
useCurrentFrame()返回當前影格,由你自行 計算數學。成熟、具型別,且僅供三人以下免費使用。
在撰寫組合程式碼之前,請載入對應的參考文件。框架規則無法猜測,且大多數失敗是無聲的: 一個從未註冊其時間軸的 HyperFrames 檔案,只會渲染出一個靜止畫面,且不會產生任何錯誤。
簡單路徑
使用者以任何詞彙描述他們想要的效果。他們永遠不需要撰寫簡報、規格或節拍表 — Claude 負責編譯。
/motion 一個為 Inkwell 製作的有力 6 秒商標動畫
這就是完整的輸入。背後執行的流程:
- 編譯 需求成
brief.json— 持續時間、尺寸、影格率、帶有時間的節拍、 逐字的文案、緩動、色板,以及一個assumptions列表。每個間隙都從references/brief.md中的表格取得預設值;沒有任何東西留空,也不對任何東西提出質疑。 - 顯示 精簡摘要(一個表格,約 12 行)並在同一次對話中建構。 沒有審批關卡。
- 從模板建構、驗證、渲染、檢視影格。
後續的迭代是修改欄位,而不是重建。「讓它更有力」會改變
feel.ease 和兩個持續時間;除非 kind 或 motion 改變,否則程式碼不會變動。
簡報是讓這個過程變得廉價的關鍵。它與組合程式碼一起存在於磁碟上, 因此修訂就是一個差異比較,而重新渲染只需要一個指令。
優先選擇路線
| 信號 | 路線 |
|---|---|
| 全新建構,未指定框架 | HyperFrames |
| 要重複使用現有的 HTML/CSS/GSAP/Lottie | HyperFrames |
不需要 node_modules,不需要建構步驟 | HyperFrames |
| 團隊人數超過三人 | HyperFrames(授權) |
| 使用者指定 Remotion,或儲存庫是 React/TS | Remotion |
| 需要設計系統、圖表庫或複雜元件 | Remotion |
| 移植現有的 Remotion 專案 | Remotion(移植成本大於節省的成本) |
當使用者同時提及兩者時,使用 HyperFrames 建構,並用一行字說明 Remotion 移植 可應要求提供。除非要求比較,否則對同一個作品進行兩次完整建構是浪費工作。
循環
1. 編譯簡報
在撰寫程式碼之前將需求轉換為 brief.json。架構、
kind/motion 語法、預設值表格和迭代槓桿都存在於
references/brief.md 中。
node scripts/brief.mjs brief.json # 驗證 + 摘要表格
node scripts/brief.mjs brief.json --json # 機器結果
將摘要列印給使用者,修正任何 ERROR,然後在同一次對話中繼續進行。
警告是需要判斷的 — 你接受的警告會放入 assumptions。
簡報也承載了節奏。選擇 audio.bpm 使節拍網格能整除剪接點 —
在 bpm 下,一個節拍是 60 / bpm 秒,並且每個 beats[].start 都應該落在
一個節拍上。120 BPM 會產生每 0.5 秒一個節拍,並且適合大多數短片。如果
brief.mjs 回報 off_beat_grid,請移動剪接點或改變節奏;不要
忽略它,因為這就是一個感覺到位與感覺微妙地不對的作品之間的差別。
完成條件 brief.mjs 退出碼為 0,每個節拍都有一個時間視窗、一種類型、一個
動畫和逐字的文案,並且沒有剪接點偏離節拍網格。
2. 建立專案骨架
從此技能的模板開始,而不是從空白檔案開始 — 它們對於 兩個合約都是正確的,並省去兩個容易失敗的步驟(時間軸註冊、序列時序)。
# HyperFrames — 純 HTML。固定的引擎會自行安裝一次,這需要網路連線
cp -R assets/hyperframes/. ./my-video/
node scripts/hf.mjs lint ./my-video
# Remotion — React 專案,需要其固定的依賴項一次
cp -R assets/remotion/. ./my-video/
cd my-video && npm ci
# PowerShell
Copy-Item -Recurse -Force assets\hyperframes\* .\my-video\
Copy-Item -Recurse -Force assets\remotion\* .\my-video\
完成條件 node scripts/hf.mjs lint 通過,或 npx remotion compositions src/index.tsx 列出組合 ID。
3. 建構節拍
每個節拍一個檔案。保持時間軸扁平 — HyperFrames 中使用絕對時間,Remotion 中使用
<Sequence> 影格。當建構改變時序時更新節拍表,然後重新檢查總持續時間。
以節拍的名稱命名每個場景,因為這是檢查找到它們的方式:
HyperFrames 宿主元素上的 data-brief-beat="<id>",Remotion 中的
<Sequence name="<id>">。如果 Remotion Sequence 自行計算時序,也在
其內部的元素上放置 data-brief-beat="<id>",以便可以測量時序。
完成條件 表中的每個節都存在於程式碼中,組合持續時間與
表匹配,並且沒有 TODOs、存根或佔位文案。
4. 驗證動畫
靜止畫面會誤導時序。先檢視影格,然後檢視渲染結果。
node scripts/check-contract.mjs --dir ./my-video --preflight # 靜態,無需安裝
node scripts/check-contract.mjs --dir ./my-video --runtime # 同時在瀏覽器中檢查
node scripts/hf.mjs check ./my-video # 佈局、動畫、對比度
node scripts/hf.mjs snapshot ./my-video --review # 節拍 + 接縫影格 PNG
node scripts/hf.mjs keyframes ./my-video --selector "#card" --shot motion.png
npx remotion still src/index.tsx LaunchCard out/f.png --frame=45
npx remotion studio src/index.tsx # 以 1 倍速觀看
check-contract.mjs 可以在不安裝任何東西的情況下捕捉無聲的失敗:未註冊的
時間軸、不匹配的 window.__timelines 鍵、未暫停的
時間軸、缺少持續時間、組合中的 Math.random()、未夾緊的
interpolate。這是一個預檢,而不是真正檢查工具的替代品。
當 brief.json 位於專案中(或 --brief 指向一個),它也會
將建構結果與簡報進行比較。每個需求最終都會是 verified、failed
或 unverified。unverified 不是通過,並且會阻止渲染。
| 代碼 | 狀態 | 含義 |
|---|---|---|
brief_format_mismatch | failed | 寬度、高度、影格率或長度與 format 不同 |
brief_scene_count、brief_scene_unmapped | failed | 某個節拍沒有聲明其 ID 的場景,或者某個場景沒有對應的節拍。位置不等同於身份 |
brief_timing_mismatch | failed | 場景的開始或持續時間與簡報不符,誤差超過一個影格 |
brief_copy_missing | failed | 該行不在場景中。註解和未使用的變數不計入 |
brief_copy_wrong_scene | failed | 該行只在另一個場景中 |
copy_not_visible | failed | 執行時期:在 DOM 中但被隱藏、透明、在畫面外、被裁剪、模糊或覆蓋 |
copy_hold_short | failed | 執行時期:可讀取時間少於 beats[].hold |
first_frame_empty | failed | 執行時期:visibleAtStart 已設定,但節拍的內容完全沒有被繪製 |
first_frame_unverified | unverified | 執行時期:第一影格只有圖形,或者節拍沒有文案。請檢視該影格 |
brief_timing_unverified、brief_format_unverified | unverified | 來源計算出這些。執行時期可以解決 |
brief_copy_dynamic、brief_copy_unverified | unverified | 是否顯示是一個執行時期的問題。在執行時期之前,Remotion 的文案始終是這個狀態 |
比對忽略大小寫和空白,並折疊彎曲引號。其他標點符號是
重要的,並且 "12,480" 會匹配一個穩定在 12480 的計數器。當需求保持
unverified 時,指出哪些,並嘗試在接受它們之前使它們可被測量。接受一個需求意味著命名它,使用
--allow-unverified "beat.lines.copy[0].visible",並向使用者報告。不要
接受通用的模式,並且失敗的結果根本無法被接受。請閱讀
references/validation.md 以了解確切的規則和限制。
修正程式碼中的偏差,或者有目的地更改簡報並重新執行 brief.mjs —
永遠不要讓兩者不一致。
審閱影格 — 所有檢查都可能通過,但影片卻是有問題的
通過合約檢查表示時間和文案匹配,並且文字是可讀的。它 沒有說明色板、緩動、轉場,或影片是否運作正常。此技能自己的模板通過了 lint、佈局、 動畫、對比度和工藝檢查,但它在每個節拍處切換到黑色畫面,並且在計數器 數字落地的瞬間就退出了計數器效果。只有檢視影格才發現了這個問題。捕捉它們:
# HyperFrames:節拍影格、每個剪接點周圍的 5 影格條、以及手機螢幕截圖
node scripts/motion-render.mjs --brief ./my-video/brief.json --snapshot --no-render
# Remotion:對於每個剪接點 c(以影格為單位),抓取 c-6、c 和 c+6,加上每個節拍的中間
#(模板在 75、210 和 270 影格處剪接)
node scripts/motion-render.mjs --dir ./my-video --no-render \
--stills 37,69,75,81,142,204,210,216,240,264,270,276,315
然後閱讀圖片並回答每個問題。修正程式碼,重新捕捉,再次檢視:
- 剪接點是黑色的嗎? 剪接點周圍有超過一個空白影格表示退場
過早完成,下一個節拍從無到有淡入。移動退場使其
在剪接點結束(
start = beat duration − exit duration)。 - 重點畫面停留時間足夠嗎? 計數器的最終數字,或一行文字的最後一個詞,
在螢幕上靜止至少一個節拍(
60 / bpm秒),然後才離開。 在它剛出現的瞬間就退場,意味著它從未被讀取。 - 在手機上看起來如何? 檢查
review-phone.jpg,或者想像影格在 寬度 360 px 時的樣子。在 1080p 解析度下,大約小於 40 px 的標籤和標語通常會失敗。 - 這是簡報指定的字體嗎? 如果顯示的是替代字體(如 Segoe UI 或 Arial),表示該字體
從未載入成功,另一台機器將渲染出不同的影片。
--runtime會警告 使用font_fallback,並且報告會記錄實際繪製的字體。Remotion 需要@fontsource/*(模板的選擇)或@remotion/fonts來使用本地檔案。 - 這是簡報指定的轉場嗎?
feel.transition應出現在每個 剪接點周圍的影格中,而不是淡出到黑色。
完成條件 你已經閱讀了節拍影格和每個接縫條,回答了上述 每個問題,並且「發布檢查清單」下的每一行都成立。
5. 渲染
node scripts/motion-render.mjs --brief ./my-video/brief.json
node scripts/motion-render.mjs --brief ./my-video/brief.json --batch rows.json --no-audio
包裝程式會從簡報中讀取路線、格式、影格率和 slug,並且在簡報、靜態檢查、工具、資源或執行時期檢查要求停止時,在花費任何渲染時間之前停止。失敗時退出碼為 1,未驗證的需求時退出碼為 3。
--no-checks 跳過驗證並在報告中記錄這個情況。然後它會進行 lint、渲染、
使用 ffprobe 測量檔案,並寫入 <out>.report.json。
當 audio.policy 為 generated 時,在任何一種路線下,它都會使用
music.mjs 合成背景音樂,僅渲染影片(不包含音訊),然後透過一個
ffmpeg 指令將兩者混合 — 這樣組合程式碼就永遠不會依賴於引擎的音訊混音器。然後它會測量
該檔案是否具有音訊串流,以及是否不是靜音的。
對於透明背景:使用 --format webm 或 --format mov — H.264 無法承載 Alpha 通道。
--no-audio 跳過背景音樂並提供一個沒有音訊串流的檔案。GIF 無法承載
音訊,因此除非你傳遞 --no-audio,否則對於包含背景音樂的簡報,系統會拒絕輸出 GIF。
使用 --batch 時,一個簡報加上一個行檔案會變成 N 部影片:HyperFrames 原生渲染
行,Remotion 為每一行獲得一個 props 檔案。批次僅限影片,並且對於包含背景音樂的簡報,系統會拒絕,除非你傳遞 --no-audio。
完成條件 檔案存在,報告顯示 rendered,其 notVerified 列表為
空,或者你已告知使用者列表中的內容,並且檔案的第一個和最後一個影格是
刻意設計的。
會無聲失效的合約
HyperFrames — 四件事,全部必須:
- 根元素帶有
data-composition-id、data-width、data-height、data-duration、data-start="0"。 - 每個有時序的元素帶有
id+data-start+data-duration。 - GSAP 時間軸使用
{ paused: true }建構。 window.__timelines[<data-composition-id>] = tl— 相同的 key。在 補間動畫被添加之後。錯過這個你會得到一個靜止畫面,沒有錯誤。
Remotion — 三件事:
- 進入檔案中的
registerRoot(),根元件中的<Composition>。 interpolate(..., { extrapolateLeft: "clamp", extrapolateRight: "clamp" })— 每次都要夾緊兩端。- 組合中不能有
Math.random()、Date.now()。請使用random(seed)。
決定影片看起來是否廉價的四條規則
這些是最常出錯的規則。完整集合及程式碼範例在
references/motion-craft.md。
- 只移動動態的部分。 靜止的東西保持不動。閒置的「呼吸」縮放 看起來像機器生成的。
- 重疊,不要排隊。 交錯約 15–30% 的元素自身持續時間。 下一件事在前一個還在穩定時就開始。
- 文字不要彈跳。 過衝適用於有質量的物體 — 卡片、翻板、
角色。文字、數字和 UI 標籤使用
power3.out/expo.out並且 永遠不要過衝。 - 每個動作只有一個想法。
y + opacity是「抵達」。加上旋轉和縮放 會讓文字在移動過程中無法閱讀。
此外:轉場應該是退場,而不是淡出後接淡入。那樣 會呈現為一個中間有下沉的跳接。
發布檢查清單
-
brief.json驗證通過:時序正確、第一影格經過設計、結尾停留 ≥ 2 秒。 -
check-contract.mjs --runtime顯示verified,或未驗證的 ID 已 按名稱接受並告知使用者。 - 文案、數字、使用者名稱和 URL 與撰寫內容一致 — 沒有 lorem ipsum,沒有佔位文字。
- 時間軸已在組合 ID 下註冊(HyperFrames),或組合已被
npx remotion compositions列出(Remotion)。 - Lint/檢查結果乾淨。
-
<out>.report.json顯示rendered,並具有預期的尺寸、影格率、長度和音訊。 - 已檢查影格:每個節拍、每個接縫、手機寬度 — 剪接點無黑色畫面,
…
[為翻譯而截斷]