Communitygithub.com

iart-ai/ecommerce-video-skills

将静态产品截图或设计转化为精美的动画演示视频,无需录屏。支持设备框架、动画光标、点击波纹、缩放平移、标注说明和场景过渡。

ecommerce-video-skills 是什么?

该技能专为生成产品演示视频设计,能够将静态 UI 截图或设计稿转化为流畅的动画演示,适合应用/SaaS 演示、功能发布、引导教程等场景。它通过代码驱动动画,确保每次渲染结果一致,避免了录屏带来的光标抖动、UI 版本漂移等问题。技能基于 Remotion 框架构建,支持多平台(如 Claude Code、Cursor 等),核心流程包括:截图框定(浏览器/设备框架)、动画光标平滑移动到目标位置(smoothstep 缓动)、点击波纹反馈、缩放至焦点区域(最大 2× 保持清晰)以及标注说明和场景过渡。输入截图需 2× 尺寸(DPR 2)以保证缩放质量,输出为 MP4 视频或 GIF。技能还提供了完整的验证流程:先生成单帧 PNG 检查准确性,再最终编码视频,确保每一帧都正确。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/iart-ai/ecommerce-video-skills/tree/HEAD/skills/product-demo-video

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

产品演示视频

将静态产品截图或设计稿转化为精致、流畅的动画演示——无需录屏工具,没有真实光标抖动,没有 UI 版本漂移。为每个画面框定设备框架,让虚拟光标平滑移动至精确像素位置,点击时产生波纹效果,然后缩放至关键区域并添加功能说明。由于输入是静态图像,运动由代码驱动,每个值都是确定性的,当截图更改时可以完全一致地重新渲染。

使用场景

  • 应用/SaaS 演示、功能发布、引导教程(时长 15–90 秒)。
  • 展示仅有的截图或设计稿中的 UI 点击路径。
  • 突出复杂仪表盘的某个焦点区域(缩放至区域 + 标注说明)。

为什么截图优于录屏

录屏截图驱动(本技能)
真实光标会漂移、超调、抖动光标沿缓动路径精确到达目标
修复一步需整段重录替换一张 PNG,重新运行相同代码
录制中途 UI 变化,鼠标寻找目标每张截图都是干净、最终状态的资源
缩放困难易出现像素化截图输出 2×(DPR 2)→ 缩放依然清晰

以显示尺寸的两倍(视网膜 / 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 倍是最佳选择——足够细节,保留上下文
添加功能说明与缩放同时保持一个好处短语,例如“一键导出”

移动镜头或光标,极少同时移动两者——两个同时运动会分散注意力。

框定截图

原始截图看起来像错误报告;框定后的截图看起来像产品。将每张画面包裹在浏览器或设备外壳中,并放置在品牌背景上。

// 浏览器框架——放入截图,得到可信的窗口
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 过渡或系统时钟——视频渲染器需要每一帧都是帧号的纯函数,否则光标会不同步并闪烁。

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

// 缓动移动从 `from` 到 `to`,在 startFrame 和 startFrame+dur 之间
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 的肯·伯恩斯效果)

要缩放至焦点元素,缩放画面并设置 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 了解聚光灯遮罩、箭头连接器、打字机效果标题轨道以及多比例(16:9 / 9:16)安全区域说明。

画面过渡

在步骤之间,使用一致的语言移动画面。选择一种并保持:

过渡方式感觉用途
推/滑“下一步”的前进感线性引导教程
交叉缩放有动感,像应用跳转到详情视图
交叉淡入淡出平静、中性切换上下文(设置 → 仪表盘)
匹配剪辑于共享元素无缝同一卡片在多个画面中持续存在

保持当前画面直到其点击动作完成,然后过渡——绝不在运动中途切换。参见 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(和任何 Web 字体)在帧渲染前已就绪——否则画面会在演示中间空白弹出。
  • 交付物 = 渲染的 out/*.mp4 文件和项目(截图更改时重新渲染)。

验证循环——渲染单帧 → 检查 → 编码。 先生成便宜的 PNG,确认无误后再生成视频。

# 使用实际要交付的属性(真实截图路径)生成精确帧 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 的保真度(正确的截图已加载;光标在精确像素位置;点击波纹和状态改变在同一帧;标题文本正确)以及伪影(图像空白/未加载、画面超出画布、缩放像素化或超出画面边界、标题超出下方安全区域、宽高比错误/黑边)。

# 仅在单帧 PNG 正确后:
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif   # README 首屏证明

批量处理(一个模板,多个产品): 当为每个产品重新设置演示时,通过单帧 PNG 验证一个代表性产品的属性,然后再批量渲染整个目录——一次捕获空白图像或超出画布的错误,而不是 N 次。

完成前:

  1. 单帧在停留/点击/结束处渲染清晰——无错误,截图已加载(非空白)。
  2. 光标精确落在目标上;波纹 + 状态改变在同一帧;标题文本正确且在下方安全区域内。
  3. 所有运动由帧驱动——无 CSS 过渡/定时器/Date.now()/Math.random()。
  4. 实际交付的属性(真实截图路径)正确渲染,不仅仅是 defaultProps。
  5. 完整的 MP4 已编码并可播放;(可选)为 README 渲染了 GIF。

参考文件

  • references/screenshot-demo.md——一个完整可运行的 Remotion 演示示例:使用 Playwright 以 2× 捕获截图、浏览器/手机框架组件、多步骤合成(光标在多个画面上移动并带有点击波纹)以及渲染命令。
  • references/annotation-and-transitions.md——聚光灯遮罩、箭头/圆点标注连接器、打字机效果功能标题轨道以及四种画面间过渡的实现(帧驱动),包含多比例(16:9 / 9:16)安全区域说明。

相关技能