Communitygithub.com

動畫錄製成對比 GIF

將動畫逐幀錄製,製作左右對比的 GIF 或逐幀對照圖,用於驗收、對比改動前後,放入說明或 PR。支援 CSS 過渡、element.animate()、requestAnimationFrame、Canvas、three.js、VRM、PixiJS、Live2D。

動畫錄製成對比 GIF 是什麼?

此技能提供一套工具與流程,用於從網頁或 Canvas 應用程式中可靠地捕獲動畫幀,並生成高品質的 GIF,特別適合 before/after 對比。其核心機制是「假時鐘」(clock shim),取代瀏覽器的真實時間,讓使用者能精確控制每一幀的觸發時機,確保不同變體間的每一幀都能逐像素對齊。這避免了因真實時間差異導致的畫面錯位,即使機器渲染速度較慢也能保持一致性。 技能包含由 Node.js 驅動的無頭 Chrome 錄製腳本 `record.mjs`,可設定 FPS、時長、裁切區域,並能透過 `--variant` 參數處理多個變體(如開關狀態)。後處理則由 Python 腳本 `make_gif.py` 負責,能將不同變體並排顯示、加入標籤,並使用共用調色板避免閃爍。此外,還提供 `clock-shim.js` 用於注入頁面,以及 `hook` 模式用於同時記錄數值數據。 此技能適用於任何需要在視覺上驗證動畫效果的情境,例如 CSS 動畫、Canvas 遊戲、three.js 或 VRM 角色動作。它與靜態截圖工具(如 `frontend-verification`)或宣傳影片工具(如 `motion-showreel`)形成互補,專注於提供精準、可對比的動畫記錄。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RedCarpG/redcarpg-skills/tree/HEAD/skills/animation-capture

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

把動畫錄成對比 GIF

動畫的毛病和改進,靠一張截圖看不出來,靠嘴說使用者也無法判斷。要把它錄下來:同一段動畫,改之前、改之後(或者開關關、開)按同一條時間軸一幀一幀錄,並排放成 GIF,使用者一眼就能拍板。

這裡的核心只有一條:時間由我們推,不由真實時鐘走。 無頭瀏覽器畫得慢(軟體渲染時尤其慢),一秒可能只畫十幾幀;用真實時間錄,每次停的位置都不一樣,左右兩邊永遠對不上。所以錄的時候讓頁面裡的時間只在我們說「往前走 40 毫秒」時才走,然後截一張,再走,再截。這樣不管機器多慢,第 n 幀永遠是第 n × 40 毫秒,兩個變體逐幀對得上,同一個變體錄兩次也一模一樣(實測 75 幀全部逐位元組相同)。

1. 先想清楚:GIF 給人看,數給檢查用

GIF 適合驗收、對比、放進說明裡,不適合找毛病。實際碰過:慢慢舉手時膝蓋一直彎著 18° 撐了一秒,這是逐幀記角度查出來的,GIF 上看不出來。所以:

  1. 先逐幀記關鍵數值(角度、位置、高度、透明度、腳有沒有滑),開關前後各一遍,看數對不對;
  2. 數對了,再錄 GIF 給人拍板。

記數值的方法見 references/hook.md(頁面裡寫一個錄製函式,錄畫面的同時把每幀的數一起交回來)。

2. 選時鐘

時鐘什麼時候用頁面要不要改
shim(預設)幾乎都用它:CSS 過渡、CSS 動畫、element.animate()、rAF 寫的 JS 動畫、canvas、three.js、PixiJS、Live2D不用改。clock-shim.js 在頁面腳本之前注入,接管 performance.now、Date.now、requestAnimationFrame、setTimeout/setInterval,Math.random 用固定種子;CSS / WAAPI 動畫每幀停住、撥到同一時刻
hook要一邊錄一邊從模型裡讀數(逐幀數值),或者應用自己有一個能按固定步長推的迴圈、想繞開瀏覽器截圖要在頁面上寫 window.__capture(...),見 references/hook.md

不要用 Chrome 自帶的虛擬時間(Emulation.setVirtualTimePolicy):試過,rAF 拿到的時間和 performance.now 對不上(計數器出現負數),動畫也不按給的步長走。

假時鐘管不到的:Web Worker 裡的動畫、OffscreenCanvas 在 worker 裡畫的、<video>、new Date()(只接管了 Date.now)、crypto.getRandomValues。碰到了改用 hook,或者在測試頁裡把它們換掉。

3. 步驟

  1. 準備頁面和變體。 變體最好用 URL 參數切(?v=off、?v=on),頁面讀參數決定開關;或者用 --setup 在開始錄前執行一段 JS 切換。只能在本機用的東西(別人做的模型、測試頁)放在不進 git 的地方,用完刪掉。
  2. 起頁面。 本地檔案直接用 file:///…;要打包的(Vite 等)起一個開發伺服器,埠挑空閒的(同一臺機器上別的對話可能佔著測試埠,別動人家的視窗)。Vite 第一次開啟新頁面會最佳化依賴、然後自己重新整理一次,錄到一半頁面被刷掉會報「window.xxx 是 undefined」:先用瀏覽器開啟一次預熱,或者報錯後重跑一次。
  3. 錄。
    node <skill>/scripts/record.mjs --out frames --fps 25 --seconds 3 --clip "#stage" \
      --ready "window.model != null" --setup "window.model.motion('TapBody', 0)" \
      --variant "關=http://127.0.0.1:5173/lab.html?v=off" --variant "開=http://127.0.0.1:5173/lab.html"
    
    • --clip 只截那一塊(CSS 選擇器);不寫就截整個視窗(--width、--height 定大小)。
    • --ready:模型、貼圖是非同步讀的,寫一個「讀好了」的表達式。等的時候假時鐘不走:走了的話每次讀得快慢不同,開始錄的時刻和隨機數用到哪都不一樣,兩次錄的對不上(實測過)。讀的過程要靠 setTimeout 的頁面才加 --ready-advance 1。
    • --wait:讀好以後先走多少假時間(毫秒),讓入場動畫、彈簧停穩;--setup 在它之後執行,執行完馬上開始錄。--setup 裡可以 await,回傳值會被丟掉(three.js 的物件太大,CDP 傳不回來)。
    • 腳本自己起無頭 Chrome(單獨的配置目錄),錄完自己關,別留著。
    • 預設用顯示卡渲染(--render gpu),開頭會打出「WebGL 渲染:…」。如果打出來的是 SwiftShader,說明沒用上顯示卡,CPU 會被吃滿好幾個核,錄 three.js / VRM 時使用者會看到 Chrome 一直佔 CPU。沒有顯示卡的機器,或者要和以前用軟體渲染錄的逐幀比較時,加 --render cpu。
    • 顯示卡和軟體渲染畫出來的邊緣不一樣,逐幀比較只在同一種渲染裡做。
  4. 先看對照圖,再出 GIF。
    python <skill>/scripts/make_gif.py frames --variants 關,開 --fps 25 --width 300 --title "Hiyori · 摸頭" --sheet sheet.png --out compare.gif
    
    --sheet 把幾幀(預設均勻取 4 幀,--pick 0,10,20 指定)排成一張 PNG:自己先讀這張圖,確認動起來了、左右對得上、取景沒裁掉東西,再把 GIF 給使用者。GIF 裡所有幀共用一個調色板,不會一幀一幀閃色;標籤寫在畫面上方的白條裡,不壓在內容上。
  5. 交給使用者:GIF 用能發檔案的工具發過去(一次發幾張有代表性的,其餘說放在哪個資料夾);說清楚每張左邊、右邊是什麼,錄的是哪個版本,哪些沒驗(比如「用的是軟體渲染,邊緣比顯示卡渲染略粗」)。

4. 參數怎麼定

  • 25 幀每秒、每張 2–4 秒;單格 300 像素寬。兩格並排的一張大約 0.6–3 MB(角色、漸層多的大一些)。
  • 動作長度按動作本身來:hook 裡可以用片段長度加一點尾巴(停下以後的餘勢也要錄到)。
  • 取景按包圍盒:三維角色用 Box3.setFromObject(scene) 算到頭頂,只按頭骨算會把大頭盔、呆毛裁掉(實測過);腳下也留一點。
  • 每個變體除了要比的那一處,其他都一樣:同一個模型、同一個相機、同一個動作、同樣的等待。隨機的部分假時鐘已經用固定種子,但頁面在不同變體裡如果多調了幾次 Math.random,後面的序列會錯開,要心裡有數。

5. 各種頁面的做法

  • 網頁(CSS、WAAPI、rAF):頁面不用改。要錄互動(點開選單、懸停),在 --setup 裡觸發(加 class、dispatchEvent)。實測:rAF 計數器、50 毫秒後才觸發的 CSS 過渡、CSS 關鍵幀動畫、element.animate() 在假時鐘下都準,兩個緩動曲線的變體逐幀對得上。
  • PixiJS / Live2D:頁面不用改,Pixi 的 ticker 用的是 rAF 和 performance.now,跟著假時鐘走;物理也按這個時間算。Live2D 的 Cubism 執行庫(live2dcubismcore.min.js)要用普通 <script> 放在模組前面先載入,引擎在被 import 時就檢查它,晚了直接報「Could not find Cubism runtime」。實測 Hiyori 的物理開、關兩個變體,兩次錄 75 幀逐位元組相同。
  • three.js / VRM:頁面不用改,自己的 rAF 迴圈跟著假時鐘走(實測 VRM 桌寵類的頁面直接能錄,開關前後的差別都在)。要逐幀讀骨頭角度時改用 hook,見 references/hook.md。
  • 遊戲、別的 canvas 應用:同上。如果應用用 setTimeout 驅動遊戲迴圈,也在假時鐘管轄裡。

6. 許可和隱私

  • 別人做的模型、素材錄出來的 GIF,要看它的許可:禁止再分發的(如 VN3 的一些條款)只能自己看、給人看效果,不要放進公開倉庫或公開發布;允許使用但要署名的,公開時寫上作者。
  • GIF 和幀放在不進 git 的地方(專案裡專門的本機資料夾,或臨時目錄),用完把測試頁刪掉。

7. 和別的 skill 的關係

  • 靜態介面截圖、多視口、深色模式:frontend-verification(它的 shot.mjs 是真實時間截圖;要錄動畫用這裡)。
  • 做給觀眾看的宣傳片、帶配樂的 MP4:motion-showreel。
  • 角色動畫本身怎麼做得自然(帶動層、物理、動作片段):live2d-vrm-animation。

8. 來源與維護

  • 來源:Cirra(桌寵,RedCarpG/cirra-deskpet)2026-11-10 做 3D 人物的帶動層、胯和腿、頭髮甩動時,為了給使用者看開關前後的對比而摸索出來的;當時的做法在那個倉庫的 docs/vrm-motion.md §11–§12、docs/vrm-spring-force.md §8(「怎麼驗」)。經驗記在私人 repo 的 lessons/animation-capture.md(三維角色那部分也在 lessons/threejs-vrm-animation.md)。
  • 源頭:RedCarpG/redcarpg-skills 的 skills/animation-capture/。~/.claude/skills/ 裡的是安裝出來的副本,不要直接改。
  • 怎麼改:
    • 在 redcarpg-skills 裡改,走 PR;manifest.json 版本加一,node scripts/validate.mjs 通過後發版(tag animation-capture-v<版本>),再重裝本機副本。
    • 改了腳本,在一個 CSS 頁面、一個 Live2D 或 three.js 頁面上各錄一次,確認兩個變體逐幀對得上;同一個變體錄兩次逐幀相同(用 Pillow 的 ImageChops.difference 比)。
    • 改了做法或坑,同步改 lessons 檔案;測試用例在 evals/。
    • 下面加一行更新記錄。
  • 更新記錄:
    • 2026-11-12:1.0.0,搬進 redcarpg-skills 公開發布。改掉本機絕對路徑,內容不變。
    • 2026-11-10,Cirra:初版。record.mjs(shim、hook 兩種時鐘)、clock-shim.js、make_gif.py。在網頁(CSS 過渡、CSS 動畫、WAAPI、rAF)、Live2D(Hiyori,物理開關)、three.js VRM(帶動層開關)上實測過。
    • 2026-11-10,Cirra:測試時查出,頁面載入時開始的 CSS / WAAPI 動畫在假時鐘接住前已按真實時間跑了幾毫秒,兩個變體差 16.6 ms;定時器觸發的過渡要等這一步走完才被接住(50 ms 變 80 ms)。改成:第一次看到的動畫從 0 算,每次跑完定時器、rAF 和每步開始時都看一遍。修後實測:50 ms 觸發的 600 ms 過渡在 80 ms 時正好 8.0 px,兩個變體第 0 幀相同。兩個測試用例(網頁緩動對比、彈球擠壓變形)有、無 skill 都全過:這類任務模型本來會做,skill 省的是每次重寫錄製腳本的工夫,以及上面這些已經踩過的坑。
    • 2026-11-10,Cirra:錄頭髮甩動(離心力、被拖著)的對比 GIF,Yumeka、千駄ヶ谷篠;三維角色用轉盤(把人放進一個能轉的 Group)和假時鐘裡的 setInterval 驅動,頁面裡不寫錄製程式碼。
    • 2026-11-10:按新的寫法規矩,「維護」一節改成「來源與維護」。
    • 2026-11-10,Cirra:使用者發現錄 3D 時 Chrome 一直佔很多 CPU。record.mjs 改成預設用顯示卡渲染(--render gpu;--render cpu 還是 SwiftShader),開頭打出用的是哪個渲染器;Chrome 退出前刪設定目錄會 EPERM,現在刪不掉就算了,不報錯。實測:RTX 3060 Ti 上一幀著色器負載從 154 毫秒降到 8 毫秒;顯示卡渲染錄兩次,10 幀逐位元組相同;顯示卡和軟體渲染逐幀不同(邊緣),不能混著比。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能