Site Reels: 網站 → MP4 → 簡報
需求
| 需要 | 項目 | 檢查方式 |
|---|---|---|
| 所有功能 | Windows、macOS 或 Linux,需有 GPU 和可見桌面(Chrome 以有頭模式執行) | 不適用 |
| 所有功能 | Node.js 18+ | node -v |
| 所有功能 | ffmpeg + ffprobe 需在 PATH 中(或設定 FFMPEG=<完整路徑>) | ffmpeg -version |
| 所有功能 | Chrome、Chromium 或 Edge(自動偵測,詳見下文) | node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))" |
record.mjs | puppeteer-core + puppeteer-screen-recorder(npm,在專案中) | npm ls puppeteer-core puppeteer-screen-recorder |
snap.mjs | puppeteer-core + timesnap(內含 timeweb) | npm ls timesnap |
| 展示影片 | Remotion 專案 | npx remotion --version |
| 審計(選用) | ImageMagick | magick -version |
| Figma 上傳 | Figma MCP(use_figma、upload_assets)+ 技能 figma-use、figma-use-slides | MCP 工具列表 |
Chrome 偵測(scripts/find-chrome.mjs,由 record.mjs 和 snap.mjs 使用):如果設定了 CHROME 環境變數則使用該路徑;否則使用已安裝的 Chrome/Edge(Windows:Program Files 和 LocalAppData;macOS:/Applications 和 ~/Applications;Linux:PATH 中的 google-chrome、chromium、microsoft-edge);否則使用最新的 ms-playwright/chromium-<N>(或 PLAYWRIGHT_BROWSERS_PATH);否則停止執行並告知如何設定 CHROME 或執行 npx playwright install chromium。在 Windows 上會加上 --use-angle=d3d11;在其他作業系統上 Chrome 保留預設的 GPU 後端。
功能說明
錄製在本地端運行的網站之腳本化場景,並產出準備好可用於 Figma 的 MP4 檔案(1920×1080、30fps、x264、小於 100MB)。驗證過的引擎:puppeteer-core + puppeteer-screen-recorder(CDP screencast + ffmpeg)。絕不進行螢幕錄製。
執行
mkdir <proj>-reels && cd <proj>-reels && npm init -y && npm i puppeteer-core puppeteer-screen-recorder
mkdir -p scripts && cp <skill-dir>/scripts/{record,find-chrome}.mjs scripts/
cp <skill-dir>/scripts/scenes.example.mjs scenes.mjs # 編輯場景
# 在網站 repo 中:npx next build && npx next start -p 3100 (生產環境建置:WebGL 更流暢)
OUT="<輸出資料夾>/Videos" node scripts/record.mjs scenes.mjs [scene ...]
執行已複製到專案中的腳本:ESM 會從腳本所在的資料夾解析 puppeteer-core,而非從當前工作目錄。
環境變數:BASE(預設 http://localhost:3100)、OUT、CHROME、FFMPEG、W、H、DPR、MOBILE=1(觸控模擬)、KEEP_UI=1(保留導覽列)、MIN_FPS(24)。手機 4:5 比例:W=360 H=450 DPR=2 MOBILE=1 產生 720x900。
每個場景 = { name, path, theme?, trim?, crf?, css?, prepare(page, ctx), act(page, ctx) }。prepare 在錄製前執行(滾動到區段、等待 3D 載入);act 則是錄製到影片中的內容。ctx 包含 wait、scrollTo(page, y|selector, ms) 和 scrollThrough(page, selector|null, ms, from, to)(含緩動效果)。載入時執行的開場:在 act 中使用 page.reload(),並使用 trim 剪掉載入過程。
有效的品牌腳本:標誌/英雄區開場(淺色和深色)、英雄媒體切換、3D 區段(鏡頭隨滾動進入)、沈浸式路線(漫步)、頁尾字標和一次全頁瀏覽。5–7 個場景,每個 12–55 秒。
交付前審計(請勿信任 exit code)
- 每個影片的聯絡表:
ffmpeg -i x.mp4 -vf "fps=6/<dur>,scale=480:-1,tile=3x2" -frames:v 1 sheet.png,然後仔細查看。 - 19 秒的 1MB 影片 = 空白場景。大於 100MB 則無法放入 Figma(可用場景的
crf修正;顆粒和拖尾場景使用 24)。 - 在數個時間點檢查頂部條狀區域(
crop=1920:120:0:0)以捕捉外漏的導覽列。 LOW-FPS並在開始/結束時有長時間暫停是假警報(計量器會計算靜止幀);請檢查聯絡表。
已知陷阱
- 隱藏視窗會凍結 rAF → 導致畫面凍結。啟動時已傳入
--disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusion。刻意使用headless: false:無頭模式會回退到 SwiftShader,WebGL 會卡頓。 - 隱藏 UI 的 CSS 在重新載入及 React 水合清除
<head>時會消失 → 腳本會每 100 毫秒重新附加一個<style id="reels-hide">到 body。 - 一個無效的選擇器會使整個規則失效:在 JS 模板字面量中的
\[會變成[,而.z-[60]會使header, nav, .z-[60]失效。每個有風險的選擇器使用單獨的規則。 - 不要隱藏
[data-cursor]:網站會用它來標記 3D 畫布和輪播。請定位自訂游標元素(fixed + mix-blend + pointer-events-none)。 - 懶加載的 3D(IntersectionObserver + 動態匯入):在到達該區段後等待 5–7 秒。沒有
preserveDrawingBuffer時,讀取 WebGL 畫布會回傳空白:請使用固定等待或螢幕截圖。 - Screencast 每秒傳送約 32 個唯一幀 → 以 30fps 錄製(60 會產生不規則的重複幀)。預設
trim0.3 秒會刪除第一個快取幀。 - 在負載較重的機器上(Photoshop 開啟、並行渲染)Screencast 會卡頓:MP4 會顯示「30fps」,但實際每秒只有 9–18 個唯一幀。
record.mjs會測量(mpdecimate=hi=64:lo=24:frac=0.05)並在低於MIN_FPS(24)時重新錄製最多 2 次。對於 WebGL/3D 畫面,建議使用snap.mjs。 - Cookie 橫幅:在
prepare中接受它(點擊「Accept/Aceitar/OK」等通用文字),然後再開始錄製。 - 沒有滾動的工具頁面(產生器、效果編輯器)錄製後會靜止不動:場景必須進行互動(上傳檔案到
input[type=file]、按按鈕文字點擊預設值)。自訂滑桿不會回應合成的input事件。
60fps 鎖定畫面:snap.mjs(確定性)
scripts/snap.mjs 注入 timeweb(timesnap 的虛擬時鐘:rAF、performance.now、setTimeout 和 video),每幀前進 1/60 秒並截圖。獨立於機器負載。場景:{ name, path, theme, css, dur, wait(載入 3D 的實際毫秒數), pre(滾動前的虛擬毫秒數,2000;**如果開場在載入時執行則設為 0**), start(跳過的虛擬秒數), scroll: { sel, from, to, at, len } | { bottom: [from, to] 以螢幕高度為單位 }, frame(page, t)(例如滑鼠) }。滾動是虛擬時間的函數。
npm i puppeteer-core timesnap && mkdir -p scripts
cp <skill-dir>/scripts/{snap,find-chrome,patch-timeweb}.mjs scripts/
npm pkg set scripts.postinstall="node scripts/patch-timeweb.mjs" && npm run postinstall
FPS=60 OUT=... node scripts/snap.mjs scenes-snap.mjs [scene]
- 請勿直接使用
timesnap():搭配 Next + R3F + Lenis 時,它錄製到空白頁面,並在 WebGL 英雄區留下凍結的片段(rAF 未執行)。自訂迴圈(goTo + scrollTo + screenshot)在所有環境都有效:60/60 唯一幀。 - 必須套用 timeweb 修補:它會將
video.play()替換為沒有 Promise 和playing事件的版本 → 否則video.play().catch()會拋出錯誤,頁面變成空白。 - 強制在 getContext 上設定
preserveDrawingBuffer: true(可使用preserve: false關閉)。 - 幀會寫入 OS 臨時目錄,絕不會寫入同步磁碟機(數千張 jpg 同步會出問題)。
- 不要並行進行 GPU 工作(Remotion 渲染、Photoshop):WebGL 會失去 context,只錄製到背景畫面。腳本會警告
FLASH(根據亮度判斷畫面閃爍);長段空白畫面只會出現在聯絡表中。請仔細檢查。 - 真正的流暢度審計:在全解析度下使用
magick compare -metric MAE比較相鄰幀。0 = 重複幀。 - 交付:
scripts/deliver.example.sh(複製並重新命名畫面;需要V=)會建立揭開效果、循環(loop.sh)和乾淨的 3D,將 crf 提高 3 直到小於 95MB。
上傳至 Figma Slides(隱藏,不干擾團隊)
MP4 不透過 MCP 上傳:use_figma 沒有網路功能,Plugin API 也無法從位元組建立影片;upload_assets 只接受圖片(≤ 10MB)。因此:
- 每個影片的封面:
ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg。 use_figma(技能figma-use+figma-use-slides):在最後一張投影片前使用createSlideRow(idx),row.name = 'WIP · Motion (hidden)',每個影片使用一個createSlide(idx, i)並設定isSkippedSlide = true,黑色背景,一個 1920×1080 的矩形命名為VIDEO · x.mp4,並在speakerNotes中提供指示。upload_assets搭配矩形的nodeIds→ 以 multipart POST 上傳封面圖片。- 使用者將 MP4 拖曳到矩形上(封面會被影片取代)。請清楚說明這點;絕不要承諾會自動插入影片。
getSlideGrid() 會省略被跳過的投影片:請透過 SLIDE_GRID.children[i].children 檢查結構。
品牌展示影片(Remotion):畫面 + 設計場景
對於品牌展示影片(標誌、顏色、字體、版面、宣言、模型),只從網站錄製乾淨的畫面(無文字:每個場景的 CSS 會隱藏標題/導覽列),並在 Remotion 中組合其餘部分(逐幀渲染,無抖動)。值得擁有的可重用建構區塊放在 src/fx.tsx:Mist、MistWords、Grain、KenBurns、Plate、nevoa()(薄霧)轉場,並在 brand.ts 中包含品牌資料。
- 視覺識別來自簡報,而非網站:在投影片中檢查官方字體和色板(網站可能偏離品牌指南)。
- 有效的場景:在薄霧中顯示開場語句、錄製的揭開效果、標誌建構(
evolvePath+ 在標誌點陣圖上測量的輔助線)、逐句顯示宣言、具有可變wght軸的支柱、色板帶狀顯示、字體排版、分層圖形、帶有mix-blend-difference文字標題的 3D、滾動版面欄位、1/2/3 欄模型蒙太奇、與名稱同步的產品、以字標收尾。 - 在渲染前透過靜止幀(
npx remotion still ... --frame=N+ 蒙太奇)進行審查。 - 使用
--concurrency=3渲染:在 8 並行、Photoshop 開啟的情況下,32GB 的機器會耗盡記憶體。 - 循環:
scripts/loop.sh in out [start] [dur] [crossfade]。使用magick compare -metric RMSE檢查首尾幀的接縫(< 0.5% = 不可見)。 - 壞的畫面就是廢棄物:一個旋轉的實心體顯示不出運動;為直幅畫布構圖的鏡頭在 16:9 中看起來很小。
- 快速展示影片(經審核的節奏):25–35 秒、60fps、
Series搭配在 120 BPM 網格(30 幀)上的硬切、每 3–6 幀的影像爆發、在網格上的巨大標誌裁切、閃爍的字體粗細、重新排列的色板區塊、圓形遮罩中的直幅人像、圖形爆發、動感短語、一拍模型、結束標誌停留約 2.5 秒。零標籤:文字僅作為圖形使用。慢速 1:40 的展示影片已被拒絕。 - 柔和/煙霧展示影片:錄製英雄區煙霧效果時不帶標誌或照片,黑色背景,純白色(場景設定
storage= localStorage 中的參數和block: ['assets/logo.png'];空白 PNG 必須是透明的;不要封鎖影片:損壞的影片會凍結 timeweb)。將畫面作為mix-blend-mode: multiply疊加層使用:在場景開始時開啟,反轉版本(ffmpeg -vf reverse)結束它;剪輯落在黑色上。轉場約 1.1 秒(速率 2),或半個場景變黑。 - Figma Slides 版面組合:複製投影片(跳過),將每張卡片複製 3 倍,僅顯示背景/標誌/文字,使用
download_assets(比例 2)下載每個節點,然後刪除複本。Slides 不匯出透明度:在黑色背景上匯出標誌/文字,然後在白色背景上匯出,並透過差異(ImageMagick)提取透明通道。絕不透過use_figma回傳 base64(會使上下文負載過大)。
產品展示影片(真實應用程式拍攝)
螢幕是真實拍攝的(Playwright + CDP screencast 以 2 倍速,步驟 goto/move/click/type/scroll/wait,游標軌跡儲存為 <id>.cursor.json),然後在 Remotion 中重新繪製游標。
已拒絕,請勿重複:
- 將設計好的展示影片與真實畫面拼接(插圖開場 + 拍攝的中段 + 繪製的 CTA):兩種語言,角色消失又出現,場景之間的黑色卡片打斷節奏。每個影片使用一種語言。
- 在 16:9 視窗中的小型加速產品畫面:動態消息和排名變成難以閱讀的一團。
- 會裁切畫面的縮放以及疊加在影片上的文字內容(巨大的動詞、01/07 計數器、漸層):會干擾。
- 畫面中的空等待(「waiting…」):直接跳到動作。
- 模糊的文案:必須清晰明瞭,並使用觀眾的日常口語。
有效的形式: 每個動作,先顯示文字本身,置中且簡短(2 拍),然後是完整的、未裁切的畫面,以捕捉時的寬高比顯示在置中視窗中,背景乾淨。低調的文字(粗細 700,逐詞進入搭配輕微模糊),無計數器、無漸層。在音軌網格上剪輯(14 幀 = 128.57 BPM)。最長 40 秒。
有效的技術:
- 加速擷取:
<OffthreadVideo trimBefore={from} playbackRate={rate}>並重新計算點擊幀/ rate(聲音和游標環)。 - 中途開始另一個合成:
<Sequence from={X}><Sequence from={-start}><Comp/></Sequence></Sequence>。檢查該段的第一幀:一個共享元素「飛入」時會顯得鬆散。跳過幾幀並重新放置落下的撞擊音效。 - 每個合成使用一種語言,搭配模組級時間軸:一個
globalThis標誌由它自己的、在 Root 之前匯入的入口檔案設定。 - 跨剪輯的連續音軌:在床音上使用
trimBefore,使其從前一段停止的地方繼續。 - 響度:渲染、測量並使用 2-pass
loudnorm(-14 LUFS / -1 dBTP)進行正規化,複製影片。只在最後測量:Remotion 的原始混音會超過 0 dBFS。 - 審計:每個渲染都製作帶有時間戳記的聯絡表(
fps=2,drawtext=%{pts\:hms},tile=8x6),並在每個段落連接處製作密集表(fps=6)。 - 沒有 git 的專案,被另一個工作階段同時編輯:將新的剪輯放在新檔案中,在編輯共享檔案前建立
*.bak-*備份,並告知使用者。
作品集卡片和工作室展示影片
record.mjs搭配KEEP_UI=1(卡片顯示網站原貌)和上面的手機預設值。- 從庫存檔案生成場景(僅限具有有效 URL 的公開項目);無有效 URL = 使用螢幕截圖的 Ken Burns 效果。
- 卡片套件:從結尾到開頭的交叉淡入循環、2-pass 位元率以達到 KB 上限、同時提供 VP9 WebM、第一幀的 WebP 海報。
- 帶有透明背景的網頁用標誌:
-
…(其餘內容已截斷,但已翻譯部分已完整保留)