Landing fly-over
一個固定的、經過藝術指導的著陸頁影片。每一幀都是時間的純函數(WebGL2 的 seek(t)),使用 Playwright 擷取,在真實 GPU 上以 16 子幀動態模糊渲染,並以 ffmpeg 編碼。沒有影片編輯器、沒有時間軸、沒有剪接。
你需要在使用者的機器上執行程式碼。 如果你無法做到(例如僅限聊天的環境),請直接說明:你可以描述影片,但 MP4 需要一個具有 shell 存取權限的編碼代理(Claude Code、Codex、Cursor……)。
0. 詢問輸入項目
一次詢問,在一則訊息中提出:
- 著陸頁連結。
- 品牌名稱與標誌圖案(一個方形的 PNG/SVG 路徑;先在 repo 中尋找:
logo*、favicon*、brand*)。 - 兩部分結尾文案:一句簡短陳述,加上一句斜體結尾。第一行請維持在約 22 個字元內(它在 1440px 畫面上以 74px 字級呈現)。從頁面本身的文案中提供 2–3 個選項。
- 一段 15 秒的音樂(可選)。如果他們沒有,提議尋找免版稅音樂(Mixkit 的免費授權允許商業使用無須署名;從
https://assets.mixkit.co/music/<id>/<id>.mp3下載)。切勿將下載的音樂提交到 repo 中;僅提交渲染後的 MP4。
1. 設定環境
cp -R <this-skill-dir>/engine ./flyover && cd flyover
npm install && npx playwright install chromium # 安裝的 Chrome 會自動優先使用(若存在)
cp config.example.json config.json
需要 Node 18+、ffmpeg 在 PATH 中、以及 Python 3 與 numpy 用於計分(opencv-python-headless 用於流暢度檢查)。將標誌放在 config.json 旁邊,並設定 url、brand、logo、title。
2. 檢查頁面,挑選浮起元件
node inspect.mjs # → dom.txt(包含區塊位置與頁面位置) + inspect.png
閱讀 dom.txt 並填寫 config.json:
hero.section:英雄區塊。hero.mockup:英雄區內的手機/產品/影片框架(影片開始時聚焦於此,結束卡片裁剪到它的下方)。hero.tileAnchor:英雄區的最後一個按鈕(四個標誌拼貼從其旁邊開始)。hero.footerTitle:頁尾前最後一個大標題(它在 8.0 秒時最清晰)。layers:會浮起的元件,格式為{ "sel", "role" },可選覆蓋z(靜止浮起高度)、zMax(鏡頭經過時的高度)與stand(朝鏡頭立起的角度)。角色與預設值:
| 角色 | 用途 | z → zMax · stand |
|---|---|---|
button | 英雄 / CTA 按鈕 | 6 → 28 · 0° |
chip | 徽章、藥丸標籤、分頁、小圖示 | 4 → 64 · 30° |
mockup | 英雄區的手機 / 產品 / 影片框架 | 10 → 24 · 0° |
card | 功能卡片、表格、終端機、指標儲存格 | 6 → 30 · 0° |
photo | 卡片內的圖片、螢幕截圖、程式碼範例 | 8 → 115 · 46° |
flychip | 1–2 個飛越畫面邊緣的小元件 | 4 → 200 · 54° |
testimonial | 一則引言 / 終端機卡片;顯示 34px 白色邊框 | 22 → 112 · 30° |
大部分浮起高度保持極小(20–40px)。整個頁面目標約 15–35 個元件,每個區塊幾個。巢狀元件可以:將內部元件(photo)列在它的卡片之前。
waitFor:由 API 提供的即時數字選擇器(訪客計數器、代幣計量表)。擷取會等到每個選擇器顯示非零數字後才凍結頁面;否則它們可能顯示為「0」。hide:Cookie 橫幅、聊天小工具、彈出視窗。css:任何額外的 CSS 以強制捲動驅動效果的最終狀態(例如文字在捲動時亮起)。routes:[{ "match": "**/broken.jpg*", "file": "/abs/local/copy.jpg" }]用於即時網站無法提供的靜態資源。
3. 擷取並鎖定頁面
node capture.mjs
這會向下捲動一次(觸發懶載入圖片、計數器、揭露效果),完成動畫,將影片凍結在某個影格(或其預覽圖),收合捲動固定的「跑道」區塊(sticky + 200vh 的捲動間隔原本會拍攝成空白區域),解除固定元素的固定定位,然後以 4096px 區塊儲存基底圖(Chrome 單次擷取上限為 16384 裝置像素:超過此長度的頁面會無聲重複頂部),以及每個浮起元件在透明背景上含 28px 邊距的獨立圖層,加上 capture/layers.json 記錄所有位置。
在規劃時間點之前先查看擷取結果:開啟 capture/page_*.png 和幾張 layer_*.png。檢查是否有空白影片影格、部分顯示的文字、重複區域、彈出視窗。使用 css/hide/routes 修正,然後重新擷取。在規劃時間點之前先鎖定頁面:任何區塊高度的變更都會導致後續所有內容偏移。
4. 靜態預覽供確認
node render.mjs stills 0,1.6,2.5,5,6.5,9.3,11 # → stills/contact_sheet.png
向使用者展示聯絡表,等待確認後再進行最終渲染。每個靜態畫面應顯示的內容:
- 0s:低角度在英雄區上方,mockup 清晰,四個 32px 標誌拼貼在按鈕旁邊。
- 1.6s:英雄區逐漸落在後方,第二個區塊的標題穿過畫面中央,附近的內容開始融化模糊。
- 2.5s:該區塊的照片立起並在鏡頭附近模糊;一個飛出元件離開畫面邊緣。
- 5s / 6.5s:穩定滑行;一個區塊在畫面中央清晰,下方柔和;使用者評價區塊升起並顯示白色邊框。
- 9.3s:正上方俯瞰,整個頁面呈現為一條置中的長條。
- 11s:結束卡片:徽章藥丸、第一行使用 Geist 500 字重、灰色 Instrument Serif 斜體結尾、英雄區作為 1018px 卡片顯示、標誌拼貼位於四個角落、底部有兩團柔和模糊。
5. 檢查動畫流暢度
node render.mjs video preview30.mp4 30 1 && node render.mjs cam
python3 flow.py preview30.mp4 # PASS = 滑行從不停滯或倒退,飛行從不放大
6. 配樂與渲染
python3 score.py track.mp3 auto # 找出重拍,將其定位在 9.97s;8.66s 和 9.80s 有呼嘯聲 → audio/score.wav
node render.mjs video flyover.mp4 60 16 # → flyover.mp4,以及當 audio/score.wav 存在時產生的 flyover-audio.mp4
在 Apple M 系列 GPU 上約每幀 0.1–0.2 秒(共 870 幀)。渲染引擎在 macOS 上選用 ANGLE Metal,Windows 上選用 D3D11,Linux 上選用 Vulkan;如果回報為 SwiftShader/llvmpipe,則表示你正在使用軟體光柵化器,速度會很慢。
影片(固定不變:不要因頁面而重新調整時間)
- 14.5 秒,60fps,1440x1152。飛行 0–8.2 秒 · 上升 7.75–9.25 秒 · 俯瞰 9.25–9.47 秒 · 潛入 9.47–9.97 秒 · 標題卡片 9.97–14.5 秒。
- 單一鏡頭,從不剪接。焦距 1880px,56° 傾斜角度,距離從 1360 緩和至 1700px,從不轉向。向下移動的速度是一條整合曲線(巡航比例):0 → 0.5 在 0.6 秒內,維持至 1.25 秒,1.0 在 1.6 秒內,維持至 7.3 秒,在 8.3 秒內緩和至 0.12。巡航速度經計算使頁尾標題在 8.0 秒時清晰,因此較長的頁面會滑行得更快。
- 每個像素的景深:前緣模糊 = 75·(focus−depth)/depth,後緣模糊 = 6·(depth−focus)/depth,上限 120px;從 mip 層級進行 40 個黃金角取樣,每個動態模糊子幀旋轉;近場模糊進行膨脹,使失焦的前景物體能覆蓋清晰的背景。
- 浮起的元件繞其自身中心立起(頂部邊緣朝向鏡頭)並產生陰影:同一圖案以純色 #1a2133 繪製,偏移 (0.10z, 4+0.36z),模糊 7+0.5z,透明度 26%。
- 上升:以對數尺度對梯形速度進行縮放,傾斜角度在 8.15–8.95 秒內變平,移交給一個將頁面底部邊緣固定在畫面上方 42px 處的相機。潛入:以對數尺度進行二次進出緩動,頁面頂部邊緣固定,然後下降至 y 283;一個圓角矩形遮罩從 24px 變形至 38px 圓角。英雄區以下的內容先淡入霧色,然後切為透明。
- 標題:徽章藥丸在 10.02 秒淡入(y 44);第一行從 10.12 秒開始,每 0.06 秒出現一個詞;結尾從 10.36 秒開始,每 0.05 秒出現一個詞;每個詞從 12px 模糊中上升 16px,歷時 0.34 秒。保持 2% 的緩慢蠕動。
禁止:剪接、交叉淡入淡出、滑行中的停止或加速、鏡頭轉向或橫向擺動、飛行期間放大、發光效果、結束卡片之前頁面上出現任何文字。
注意事項
- 超大頁面:基底圖以區塊儲存,從不使用單張螢幕截圖。如果在頁面中段看到英雄區重複,表示一次擷取超過了 16384 裝置像素。
- 捲動驅動的網站:粘性跑道會自動收合;逐詞滾動高亮效果需要
css規則強制設定為完全不透明。 - 影片:它們會凍結在
videoTime秒。如果即時網站的影片無法載入(404),你會看到黑色畫面;透過routes提供本地預覽圖。 - 淡出卡片:會顯示其背後的內容,因此請先淡出到霧色(引擎對英雄區以下的所有內容都會這麼做)。
- 第一行太長:文字在 74px 字級下會超出畫面。請縮短該行,而非縮小字型。
- 授權:音樂的版權責任在使用者身上;建議選擇其授權涵蓋商業影片的曲目。