Communitygithub.com

2233admin/design-pipeline

這個技能用於將靜態截圖或設計圖轉換為乾淨的動畫展示影片,包含裝置框架、動畫游標、點擊漣漪、縮放、註解與畫面轉場。

design-pipeline 是什麼?

這個技能,稱為「產品展示影片(Product Demo Video)」,旨在幫助開發者將靜態產品截圖或設計圖轉化為精美、動畫化的產品展示影片,無需使用螢幕錄製工具。它特別適合製作應用程式或 SaaS 的展示、功能發布預告、以及引導教學影片。與傳統螢幕錄影相比,此技能產出的影片每一幀都是確定性的:游標會沿著緩和路徑精準移動,點擊有漣漪效果,並可針對特定 UI 區域進行縮放和加上註解說明,且所有動畫參數皆基於幀數函數,確保每次重新渲染結果一致。主要運作於 Remotion 框架,透過程式碼驅動所有動畫,輸出為高品質 MP4 檔案。它提供了完整的製作流程:從使用瀏覽器/裝置框架包裝截圖、驅動游標移動與點擊效果、執行類似 Ken Burns 風格的 UI 區域縮放,到加入註解與畫面轉場,皆涵蓋在內。此外,技能還包含了詳細的輸出檢查清單與驗證流程,確保最終影片的品質。

相容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/2233admin/design-pipeline/tree/HEAD/skill/references/iart-motion-skills/upstream/ecommerce-video-skills/skills/product-demo-video

在你喜歡的 AI 中提問

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

說明文件

產品展示影片

將靜態產品截圖或設計圖轉變為精緻、動畫化的展示影片 — 無需螢幕錄製工具,不會有抖動的真實游標,也沒有 UI 版本漂移問題。為每個畫面加上框架,讓合成的游標滑動到精確的畫素位置,產生點擊漣漪,然後縮放到重要的部分並為該功能加上說明文字。由於輸入是靜態的,且動作由程式碼驅動,因此每個數值都是確定性的,當截圖變更時,重新渲染的結果完全相同。

使用時機

  • 應用程式 / SaaS 展示、功能發布預告、入門引導教學(15–90 秒)。
  • 展示一個僅有截圖或設計圖的 UI 的點擊路徑。
  • 強調一個資訊密集儀表板中的某個焦點區域(區域縮放 + 註解標示)。

為什麼截圖勝過螢幕錄製

螢幕錄製截圖驅動(此技能)
真實游標會偏移、超標、抖動游標沿著緩和路徑移動到精確目標
修正一個步驟需要重新錄製整個片段替換一張 PNG,重新渲染相同程式碼即可
錄製過程中 UI 改變,游標會亂跑每個畫面都是乾淨的最終狀態素材
難以放大而不失真以 2 倍(DPR 2)匯出截圖 → 縮放依然清晰

以兩倍顯示大小(Retina / deviceScaleFactor: 2)擷取輸入,這樣 2 倍縮放時仍保持銳利。請參閱 references/screenshot-demo.md 以取得 Playwright 擷取腳本。

展示循環

每個展示都遵循相同的節奏,每個功能重複一次。停留足夠長的時間以便閱讀,有目的地移動,準確點擊,然後展示結果。

節拍時間預算任務
停留在畫面上0.5–1 秒讓觀眾在動作開始前先熟悉畫面
游標移動到目標0.4–0.8 秒緩和移動(ease-in-out),非線性
點擊漣漪 + 狀態變化0.2–0.3 秒觸覺回饋;畫面在同一幀做出反應
縮放到焦點區域1–2 秒進入,停留 2–4 秒2 倍是最佳點 — 足夠的細節,保留上下文
為功能加上說明文字在縮放期間停留一個好處短語,例如「一鍵匯出」

一次只移動攝影機或游標,很少同時移動兩者 — 同時移動會分散注意力。

為截圖加上框架

原始截圖看起來像錯誤報告;加了框架的截圖則像產品。將每個畫面放在瀏覽器或裝置的 Chrome 風格框架中,並搭配品牌背景。

// 瀏覽器框架 — 放入截圖,得到一個可信的視窗
const BrowserFrame = ({ url, children }) => (
  <div style={{ borderRadius: 12, overflow: "hidden", background: "#1e1e22",
                boxShadow: "0 40px 80px rgba(0,0,0,.35)" }}>
    <div style={{ height: 40, display: "flex", alignItems: "center", gap: 8, padding: "0 14px" }}>
      {["#ff5f57", "#febc2e", "#28c840"].map((c) => (
        <span key={c} style={{ width: 12, height: 12, borderRadius: "50%", background: c }} />
      ))}
      <div style={{ marginLeft: 12, flex: 1, height: 24, borderRadius: 6, background: "#2b2b30",
                    color: "#9aa", fontSize: 13, display: "flex", alignItems: "center", padding: "0 10px" }}>
        {url}
      </div>
    </div>
    <div style={{ background: "#fff" }}>{children}</div>
  </div>
);

在網址列中放入真實 URL(增加真實性),加上柔和的陰影和漸層背景。對於手機,則切換到帶有瀏海和圓角的電話框架。將框架樣式保留在一個主題物件中,使每個畫面都一致。

動畫游標 + 點擊漣漪(核心動作)

根據當前影格驅動位置,永遠不要使用 CSS 過渡或 wall clock — 影片渲染器需要每一幀都是影格編號的純函數,否則游標會不同步並閃爍。

import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";

// 在 startFrame 和 startFrame+dur 之間,從 `from` 緩和移動到 `to`
const useCursor = (from, to, startFrame, dur) => {
  const frame = useCurrentFrame();
  const t = interpolate(frame, [startFrame, startFrame + dur], [0, 1],
    { extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => x * x * (3 - 2 * x) }); // smoothstep
  return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t };
};

export const Cursor = ({ from, to, startFrame, dur, clickAt }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const { x, y } = useCursor(from, to, startFrame, dur);

  // 漣漪在點擊影格時展開一次
  const since = frame - clickAt;
  const showRipple = since >= 0 && since < 18;
  const r = spring({ frame: since, fps, config: { damping: 18 }, durationInFrames: 18 });

  return (
    <>
      {showRipple && (
        <div style={{ position: "absolute", left: x, top: y, transform: "translate(-50%,-50%)",
          width: 12 + r * 60, height: 12 + r * 60, borderRadius: "50%",
          border: "2px solid rgba(80,140,255,.9)", opacity: 1 - r }} />
      )}
      <svg width="28" height="28" viewBox="0 0 28 28"
           style={{ position: "absolute", left: x, top: y, filter: "drop-shadow(0 2px 3px rgba(0,0,0,.4))" }}>
        <path d="M3 2 L3 21 L9 15 L13 24 L16 23 L12 14 L20 14 Z" fill="#fff" stroke="#222" strokeWidth="1.4" />
      </svg>
    </>
  );
};

游標使用 smoothstep,因此它會從靜止開始加速,並在到達目標時減速(線性游標看起來像機器人)。在同一個 clickAt 影格觸發漣漪和螢幕的狀態變化,使原因和效果對齊。請參閱 references/screenshot-demo.md 以了解在多個畫面中連接多次點擊的完整組合。

縮放到一個區域(UI 的 Ken Burns 效果)

要縮放到一個焦點元素,請縮放畫面並將 transform-origin 設定為該元素的中心,這樣縮放會收斂到該元素上,而不是畫面的中心。

const ZoomToRegion = ({ target, scale = 2, startFrame, dur, children }) => {
  const frame = useCurrentFrame();
  const s = interpolate(frame, [startFrame, startFrame + dur], [1, scale],
    { extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => 1 - Math.pow(1 - x, 3) }); // easeOutCubic
  return (
    <div style={{ transform: `scale(${s})`, transformOrigin: `${target.x}px ${target.y}px`,
                  willChange: "transform" }}>
      {children}
    </div>
  );
};

縮放上限設在 2 倍左右(超過 3 倍會失去上下文並使人迷失方向)。保持縮放狀態 2–4 秒 — 足夠的時間閱讀細節 — 然後在移動到下一個畫面之前慢慢退出。將縮放與註解標示搭配使用,絕不單獨使用。

註解標示 & 功能說明文字

在關鍵點加上註解,然後讓它消失。註解標示是一個標籤加上一個連接器(箭頭、圓圈或底線),指向焦點元素。

  • 突顯元素,使用柔和的環形或變暗的周圍環境(box-shadow: 0 0 0 9999px rgba(0,0,0,.45) 可以切出一個聚光燈孔)。
  • 說明文字每個節拍一個好處 — 簡短的片語(「即時分析」),而不是一個句子。在縮放時進入,在下一個步驟之前退出。
  • 將說明文字保留在下方三分之一處並在標題安全區域內,以便在裁切為 9:16 時仍然可見。

請參閱 references/annotation-and-transitions.md 以了解聚光燈遮罩、箭頭連接器、打字功能說明軌道。

畫面間轉場

在步驟之間,使用一種一致的語言移動畫面。選擇一種並保持不變:

轉場感覺用途
推動 / 滑動「下一步」的前進動作線性操作導覽
跨縮放有動感,像應用程式跳入詳細檢視
交叉淡出平靜、中性切換上下文(設定 → 儀表板)
匹配剪輯在共享元素上無縫相同卡片跨畫面持續存在

在點擊解析之前,保持離開的畫面,然後再進行轉場 — 永遠不要在運動中進行剪輯。請參閱 references/annotation-and-transitions.md。

輸出檢查清單

  • 輸入以 2 倍(DPR 2)擷取;縮放在 2 倍時保持清晰。
  • 每個游標/縮放值都是 useCurrentFrame() 的純函數 — 動畫狀態不使用 CSS 過渡。
  • 游標沿著緩和路徑(smoothstep)移動;漣漪和狀態變化在同一個點擊影格觸發。
  • 每個畫面都加上框架(瀏覽器/裝置),包含真實 URL 和一致的主題。
  • 一次只有一個動作(攝影機或游標);縮放 ≤ 2 倍,保持 2–4 秒。
  • 每個節拍一個好處說明文字,位於標題安全的下方三分之一處;轉場使用一種一致的語言。

交付與驗證(渲染靜止幀 → MP4)

封裝輔助工具(scripts/):使用 scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png 拼貼您的靜止幀,然後使用 scripts/probe-mp4.sh out.mp4 [WxH] [fps] 斷言編碼。請參閱 scripts/README.md。

這是重型層級:交付物是 MP4,而不是一個動態頁面。該展示引入使用者素材 — 截圖/設計、可選的 Logo — 因此驗證主要是正確的圖片是否確實載入並落到畫面中。

輸出合約:

  • 一個 Remotion 專案,包含已註冊的組合(<Composition> + zod schema + defaultProps);每個游標/縮放/轉場值都是 useCurrentFrame() 的純函數(沒有 CSS 過渡、計時器、Date.now())。
  • 透過 staticFile() 載入截圖,並使用 delayRender/continueRender 進行閘控,以便在影格渲染之前,2 倍 PNG(以及任何網路字型)就已經存在 — 否則畫面會在展示中途空白彈入。
  • 交付物 = 渲染後的 out/*.mp4 加上專案(當截圖變更時重新渲染)。

驗證循環 — 渲染靜止幀 → 檢查 → 編碼。 先檢查便宜的 PNG,只有在它們正確時才製作影片。

# 使用您將要交付的屬性(真實截圖路徑),而非 defaultProps,來產生影格精確的靜止幀
npx remotion still ProductDemo out/f-settle.png --frame=15  --props=demo.json   # 畫面加上框架,移動之前
npx remotion still ProductDemo out/f-click.png  --frame=90  --props=demo.json   # 游標在目標上 + 漣漪
npx remotion still ProductDemo out/f-zoom.png   --frame=150 --props=demo.json   # 縮放到焦點區域 + 說明文字
# 結束影格 = durationInFrames - 1(npx remotion compositions 會讀取它)

檢查每個 PNG 的精確度(正確的截圖已載入;游標在精確的畫素上;點擊漣漪和狀態變化在同一影格;說明文字正確)和瑕疵(圖片空白/未載入、畫面超出畫布、縮放失真或超出畫面邊界、說明文字超出標題安全的下方三分之一處、寬高比錯誤/出現黑邊)。

# 只有在靜止幀乾淨之後:
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif   # README 第一個畫面證明

批次處理(一個範本,多個產品): 當為每個產品重新製作展示外觀時,在批次渲染整個目錄之前,先透過靜止幀驗證一個代表性產品的屬性 — 一次就抓到空白圖片或畫面超出畫布的錯誤,而不是 N 次。

在你完成之前:

  1. 靜止幀在停留/點擊/結束時渲染乾淨 — 沒有錯誤,截圖已實際載入(非空白)。
  2. 游標落在精確目標上;漣漪 + 狀態變化在同一影格;說明文字正確且在標題安全的下方三分之一處。
  3. 所有動畫由影格驅動 — 沒有 CSS 過渡/計時器/Date.now()/Math.random()。
  4. 交付的屬性(真實截圖路徑)能正確渲染,而不僅僅是 defaultProps。
  5. 完整的 MP4 已編碼並可播放;(可選)GIF 已為 README 渲染。

參考檔案

  • references/screenshot-demo.md — 一個完整可執行的 Remotion 展示:使用 Playwright 以 2 倍擷取截圖、瀏覽器/手機框架元件、一個多步驟組合,該組合在多個畫面間移動游標並產生點擊漣漪,以及一個渲染指令。
  • references/annotation-and-transitions.md — 聚光燈遮罩、箭頭/圓圈註解連接器、一個打字功能說明軌道,以及四種畫面間轉場,附帶影格驅動的實作和多種寬高比(16:9 / 9:16)的安全區域備註。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相關技能