Communitygithub.com

demo-reel

根据简短脚本录制任何 Web 应用的流畅、帧精确 60fps 演示视频——脚本化光标带有高亮光晕、点击涟漪、字幕和角落徽章,基于虚拟时钟渲染以确保绝无卡顿,编码为可在 PowerPoint、Keynote 或网页中循环播放的 MP4。

demo-reel 是什么?

demo-reel 是一款 AI 代理技能,用于从脚本化的 Web 应用操作生成高保真演示视频。它驱动真实应用的无头 Chromium 实例,通过注入虚拟时钟来控制 requestAnimationFrame、performance.now 和 Date 的推进,而非依赖浏览器实际的渲染帧率,从而确保 60fps 的流畅输出。该工具支持脚本化光标移动、点击涟漪、字幕覆盖和角落徽章,所有元素都精确地记录在每一帧中。它适用于 Claude Code、Cursor、Codex 等 AI 编码助手,并需要 Node 18+、ffmpeg 和 Playwright。生成的 MP4 视频采用 H.264 yuv420p 编码,可直接在 PowerPoint、Keynote 和网页中无缝循环播放,避免了常规屏幕录制在低性能机器上常见的卡顿问题。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/hasuwini77/demo-reel/tree/main/skills/demo-reel

在你喜欢的 AI 中提问

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

预览

来自技能 README

文档

demo-reel 是做什么的?

将脚本化的 Web 应用操作过程转换为一段精美的屏幕录制视频:流畅的 60fps 运动、可见的带光晕光标、点击涟漪和字幕。它驱动真实应用的无头 Chromium——画面即产品本身,而非模拟图。

为何视频如此流畅

普通屏幕录制在浏览器恰好绘制时捕获帧;在慢速机器(或软件 WebGL)上,帧率大约只有 10–15fps 并会出现卡顿。而 demo-reel 注入了一个虚拟时钟(scripts/inject.js):requestAnimationFrame、performance.now 和 Date 仅在录制器要求时前进,且 CSS/Web 动画通过 document.getAnimations() 逐步推进。每一帧精确前进 1/fps,截图后通过 ffmpeg 编码。无论渲染一帧需要多少实际时间,最终视频都完美流畅。

计时器(setTimeout/setInterval)故意保持真实——伪造它们会导致加载器和调度器中的零延迟计时器链无限循环(这就是 Playwright 自带的 page.clock 在许多 WebGL 页面上挂起的原因)。

要求

  • Node 18+,ffmpeg 在 PATH 中。
  • 在技能目录中执行一次:npm install && npx playwright install chromium。

工作流程

  1. 启动应用,以用户看到的方式——最好是生产构建(vite build && vite preview,next build && next start)配合本地或模拟数据。开发服务器也可用但较慢,且可能显示开发覆盖层。切勿录制真实或敏感数据——请使用演示数据。
  2. 编写场景(*.scenario.mjs,参见下方 API)。从 examples/tasks.scenario.mjs 开始。将其规划为一个故事:20–60 秒,每个字幕一个想法,每个结果后停留 1–2 秒以便观众阅读。
  3. 录制:node scripts/record.mjs my.scenario.mjs --out demo.mp4 一段 60 秒的 1080p 片段大约需要 3–10 分钟(重型 WebGL 页面最慢)。在后台运行。
  4. 质量检查:scripts/sheet.sh demo.mp4 会生成一张联系表(每 2.5 秒一帧)。检查:字幕与屏幕内容匹配,无加载转圈,内容未被字幕遮挡,徽章/光标可见。通过提取连续几帧快速检查流畅度——在运动期间每帧应略有差异。
  5. 交付(参见“使用视频”)。

场景 API

export default {
  size: "1920x1080",          // 视口 = 视频尺寸
  fps: 60,
  theme: { captionPosition: "bottom", haloFill: "rgba(250,204,21,.30)" }, // 可选
  async run(d) {
    await d.open("http://localhost:4173/");     // 导航 + 稳定(不计入录制)
    d.badge("NEW", "#15803d");                  // 角落徽章(null 隐藏)
    d.caption("新的仪表板");                     // 字幕药丸(null 隐藏)
    await d.hold(1500);                          // 录制 1.5 秒不变
    await d.click(d.page.getByRole("button", { name: "Filters" }));
    d.caption("筛选器集中在一个面板中");         // 在操作后设置字幕
    await d.hold(1200);
    await d.type("Stockholm");                  // 模拟人类速度打字
    await d.press("Enter");
    await d.scroll(600);                        // 平滑滚轮滚动
    await d.step("可选部分", async () => { /* 错误会被记录,录制继续 */ });
  },
};
调用作用
d.open(url, { settle })导航,让页面稳定(不录制,默认 2500 毫秒)
d.hold(ms)按当前状态录制页面
d.settle(ms)推进时间但不录制——跳过加载状态、延迟加载的代码块、不想显示的布局动画
d.move(target, { ms }) / d.hover平滑移动光标(缓动)到目标
d.click(target, { ms, button })移动,点击并产生涟漪。button: "right" 用于上下文菜单
d.doubleClick(target)双击
d.type(text, { delay })逐键输入(默认 90 毫秒/键)
d.press(key)键盘快捷键,如 "Escape","Control+K"
d.scroll(dy, { ms })平滑滚轮滚动
d.caption(text) / d.badge(text, color) / d.cursor(bool)覆盖层状态;跨完整页面导航保持不变
d.step(name, fn)命名步骤;错误会被记录,不影响整体
d.page,d.point(target),d.width,d.height逃生舱

目标可以是 Playwright Locator、CSS 选择器字符串、{ x, y } 或异步函数 (page) => ({ x, y })。

Canvas 渲染的 UI(图形、地图、图表)没有每个项目的 DOM 元素——传递一个函数,让库返回屏幕坐标。Cytoscape.js 示例:

const node = (label) => (page) => page.evaluate((label) => {
  const el = [...document.querySelectorAll("div")].find((e) => e._cyreg?.cy);
  const n = el._cyreg.cy.nodes().filter((n) => n.data("label") === label)[0];
  const r = el.getBoundingClientRect(), p = n.renderedPosition();
  return { x: r.left + p.x, y: r.top + p.y };
}, label);
await d.click(node("Server A"));

编写优秀演示的技巧

  • 字幕要真实。 只声称画面实际展示的内容。如果想展示的 bug 在录制中未复现,修改字幕——不要保留它。
  • 在操作后设置字幕,而不是操作之前——否则下一屏的字幕会出现在上一屏上。
  • 展示结果,而非点击。 在每个结果上停留 1–2 秒;用 d.settle 跳过加载。
  • 对比前后:用相同的故事情节和步骤录制两个场景,徽章分别为 BEFORE/AFTER(红色/绿色),保存为单独文件并可选拼接。
  • 高光时刻(如落地页、动画):给予 5–10 秒——观众需要时间消化。
  • 保持光标安静:每次移动 600–900 毫秒,在保持期间将其远离内容。
  • 真实定时器在帧渲染期间持续运行(每帧约 50–300 毫秒实际时间),因此由定时器驱动的 UI——如提示、防抖搜索、自动关闭的工具提示——在视频中可能比现实中消失得更快。在需要它们显示之前触发,或减少保持时间。

使用视频

  • PowerPoint:插入 → 视频 → 此设备。播放选项卡:开始:自动 并 循环播放直到停止。H.264 yuv420p MP4(demo-reel 输出的格式)在 Windows 和 macOS 上均可播放。
  • Keynote:拖入;影片 → 重复 → 循环。
  • 网页:<video src="demo.mp4" autoplay loop muted playsinline></video>。
  • GIF(README、聊天):ffmpeg -i demo.mp4 -vf "fps=20,scale=960:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" demo.gif
  • 拼接同尺寸/帧率的片段:printf "file 'a.mp4'\nfile 'b.mp4'\n" > l.txt && ffmpeg -f concat -safe 0 -i l.txt -c copy both.mp4

故障排除

症状解决方法
空白 / "Loading…" 帧在 d.open 中延长 settle,或在导航点击后使用 await d.settle(1500)
目标不可见目标在屏幕外或关闭的菜单内——先展开/滚动;对于 Canvas 项目使用点函数
WebGL/3D 渲染为黑色保留默认启动标志(--use-angle=swiftshader);虽然慢(约 0.25 秒/帧)但正确
视频比计划短或长持续时间精确:hold + 移动的总和。settle 时间从不计入录制
在 PowerPoint 中颜色显得暗淡不要在没有 -pix_fmt yuv420p 的情况下重新编码;demo-reel 的输出已经正确
文字略微模糊使用 --png 捕获无损帧(更慢,文件更大)

相关技能