動畫 OG 圖片
此技能使用的所有檔案都存放在自身資料夾(與此 SKILL.md 相鄰)中,路徑皆相對於該資料夾。此技能不需依賴其他技能或資料夾,請勿四處尋找。除非使用者指定其他位置,否則所有產出都儲存在目前專案的 out/og-image-animated/ 目錄下。
靜態分享卡片以短暫、平緩的揭曉動畫呈現:
- 空卡片:僅有背景與柔和光暈。
- 品牌標誌和標題從頂部落入(淡入 + 向下滑動)。
- 應用程式視窗從底部升起(淡入 + 向上滑動)並準確定位到靜態卡片上的位置。
- 停留於最終幀,該幀必須與靜態 OG 圖片完全相同。
範例:references/pulse.gif(結果)與 references/pulse-frames.png(四個幀:開始、標題進入、應用升起、最終成品)。
規則
- 從靜態卡片開始。 如果專案尚未有 OG 卡片元件,請先遵循
references/static-card.md建立(使用真實產品、目前網站的訊息以及實際資料)。動畫僅加入動作:使用相同標記、相同最終佈局,不加入新內容。 - 單一元件,兩種模式。 在現有卡片元件上新增
animated屬性,而不是複製一份,以確保靜態 PNG 與動畫永不偏離。沒有該屬性時,則不會有任何動畫效果。 - 僅動作,無特效。 只使用
opacity+transform: translateY,並共用一個緩出曲線(cubic-bezier(0.16, 1, 0.3, 1))。無彈跳、模糊、縮放彈出、打字機文字或旋轉元素。 - 簡短。 整個時間軸約 2–2.5 秒,之後停留約 1.5 秒。標題必須在約 1.3 秒時完全可讀。
- 僅使用 CSS 動畫(
keyframes+ 帶有both填充的animation),不可在卡片內使用 JS 驅動的補間或計時器。錄製器透過 Web Animations API 擷取 CSS 動畫;JS 驅動的動作無法被確定性捕獲。
時間軸
| 元素 | 關鍵幀 | 持續時間 | 延遲 |
|---|---|---|---|
| 品牌標誌 | 從 opacity: 0; translateY(-48px) | 900ms | 100ms |
| 標題 | 從 opacity: 0; translateY(-48px) | 1000ms | 250ms |
| 應用視窗 | 從 opacity: 0; translateY(320px) | 1300ms | 700ms |
根據卡片調整距離:應用程式應從底部邊緣下方(約卡片高度的一半)開始,標題則應從其靜止位置上方自身高度處開始。如果存在浮動面板,則讓它在視窗開始後 +200ms 抵達。
步驟
1. 探索
- 找到靜態卡片元件及其渲染路由(例如
og-image.tsx、/og/<page>),以及public/中的目前 PNG。 - 記下品牌標誌、標題和應用視窗的包裝器。如果視窗使用
zoom來適配,則對內部包裝器進行動畫處理,而不是已縮放的元素,以確保偏移量正確。
2. 加入動畫模式
在卡片元件中:
const KEYFRAMES = `
@keyframes og-drop { from { opacity: 0; transform: translateY(-48px); } to { opacity: 1; transform: none; } }
@keyframes og-rise { from { opacity: 0; transform: translateY(320px); } to { opacity: 1; transform: none; } }
`;
const EASE = 'cubic-bezier(0.16, 1, 0.3, 1)';
const enter = (name: string, ms: number, delay: number) => ({ animation: `${name} ${ms}ms ${EASE} ${delay}ms both` });
// <div data-og-frame …>{animated && <style>{KEYFRAMES}</style>} …
// brand: style={animated ? enter('og-drop', 900, 100) : undefined}
// title: style={animated ? enter('og-drop', 1000, 250) : undefined}
// window: <div style={{ zoom }}><div className="h-full" style={animated ? enter('og-rise', 1300, 700) : undefined}>…
將關鍵幀放在元件(<style> 標籤)或專案的全局 CSS 中;使用 Tailwind v4 時,@theme 內的關鍵幀僅在使用了對應的 --animate-* token 時才會被產生,因此建議將其放在元件或純 CSS 中。
3. 加入播放動畫的網頁路由
作為靜態路由的同級路由,例如 /og/<page>-animated(設定 noindex,無應用程式外框):
- 在 1200×630 的框架內渲染
<Card animated />。 - 預覽時重播:透過每隔約 4.5 秒更換
key來重新掛載卡片。 ?loop=0只播放一次(錄製器使用此參數)。首先將搜尋參數正規化為字串:像 TanStack 這樣的路由器會將?loop=0解析為數字0,而一項漏掉的檢查會導致在錄製過程中不斷重新掛載卡片。
在瀏覽器中開啟它並觀看幾次。檢查進入順序,確保動畫開始前沒有任何閃爍,並且最終狀態與靜態卡片相符。
4. 錄製 MP4 + GIF
在開發伺服器運行時:
node <此技能資料夾>/scripts/record.mjs \
--url "http://localhost:<port>/og/<page>-animated?loop=0" \
--out out/og-image-animated/og-<page>
寫入 og-<page>.mp4(2400×1260,H.264,30fps)、og-<page>.gif(1200px 寬,調色板優化,循環播放)以及 og-<page>.png(最終幀)。運作原理:它暫停頁面上的所有動畫,讀取時間軸長度,然後逐幀尋找所有動畫並對 [data-og-frame] 區域進行螢幕截圖。每次執行的結果都相同,不會掉幀。選項:--fps、--hold、--lead、--scale、--formats mp4,gif,png、--selector。
需要在 PATH 中有 ffmpeg。Playwright 會在首次運行時安裝到此技能的 scripts/ 資料夾中,如果找不到 Chromium 也會一併安裝。
注意事項:
- 頁面不得強制啟用減少動畫模式。許多全局樣式表會在
prefers-reduced-motion下縮短動畫;錄製器不會模擬此設定,而且如果找不到任何動畫,它會大聲報錯。 - 元素螢幕截圖會等待「穩定」,但動畫進行中永遠不會穩定。這就是為什麼腳本會裁剪頁面區域。
- 請確保連接埠確實正在為此應用程式提供服務(另一個本地伺服器可能回應
localhost)。
5. 審查
提取一個聯絡表並查看:
ffmpeg -y -loglevel error -i og-<page>.mp4 \
-vf "select='eq(n\,8)+eq(n\,22)+eq(n\,40)+eq(n\,<last>)',scale=1000:-1,tile=2x2" -frames:v 1 sheet.png
- 大約第 0.25 秒:空卡片(僅光暈),無佈局跳動。
- 大約第 0.7 秒:標題已進入或接近進入,應用程式尚未可見。
- 大約第 1.3 秒:應用程式正在升起,標題完全可讀。
- 最後一幀:內容與靜態 PNG 完全相同。
保持 GIF 大小合理(低於約 3MB)。如果太大,請降低 --fps(20–24)或縮短 --hold。
輸出
告知使用者:
- 預覽路由 URL(循環播放)以及寫入的檔案(MP4、GIF、PNG)
- 重新產生指令
- 時間軸(哪些元素移動、持續時間)
og:image必須保持為靜態 PNG:社群平台不播放動畫 OG 圖片。請將 MP4/GIF 用於貼文、README、發布頁面或支援og:video的地方。