使用 bueno-demo 制作演示视频
bueno-demo 录制一段脚本化的网页操作流程,并将其渲染为 Screen Studio 风格的视频片段。流程分为两步:捕获驱动真实的 Chromium 浏览器窗口并记录操作,渲染则添加摄像机、光标和标题效果。
初始化设置(每台机器执行一次)
npm i -D bueno-demo # 或直接使用 npx bueno-demo 运行所有命令
npx playwright install chromium
brew install ffmpeg # 必需;如需 WebP 格式海报需额外安装 imagemagick
工作循环
- 编写脚本:创建一个 JSON 文件(见下文)。每个片段聚焦一个功能点,时长控制在 10 到 15 秒。
- 捕获:
npx bueno-demo capture demos/<name>.json out/<name> - 可选裁切(用于处理缓慢加载的情况):
npx bueno-demo cut out/<name> <from-s> <to-s> - 渲染:
npx bueno-demo render out/<name> out/<name>/<name> --gif - 使用前务必检查。 生成联系表并逐帧检查:
ffmpeg -i out/<name>/<name>.mp4 -vf "fps=1,scale=640:-1,tile=3x4" -frames:v 1 sheet.png如果发现 Bug 或敏感数据出现在画面中,修改脚本或使用 cut 命令裁切后重新渲染。
脚本结构
{
"url": "http://localhost:3000/settings",
"viewport": [1440, 900],
"storage": "auth.json",
"keep": "document.querySelectorAll('[data-user-email]').forEach(e => e.textContent = '[email protected]')",
"steps": [
{ "scroll": "[data-testid=billing]" },
{ "click": "[data-testid=plan-toggle]", "after": 1200 },
{ "type": "[data-testid=team-name]", "text": "Northwind", "clear": true },
{ "hover": "[data-testid=save]", "zoom": true },
{ "click": "[data-testid=save]", "zoom": false },
{ "wait": 800 }
]
}
动作步骤,每个步骤可附加 after(该步骤执行后暂停的毫秒数):
click:滑向目标元素并点击。zoom: false跳过摄像机缩放;适用于点击后发生页面跳转的场景。type:点击元素,然后逐字符输入text(delay参数控制每键间隔毫秒数,默认 55;clear: true会先清空现有内容)。fill:点击元素,然后一次性设置text,适用于输入框类型无法驱动的场景(如日期选择器)。hover:滑向目标元素。zoom: true会使摄像机视口也跟随移动。upload:滑向拖放区域,将file(相对脚本路径)附加到input元素。press:按下某个键,例如"Escape"。scroll:平滑滚动页面,使指定元素进入视图。wait:暂停,单位毫秒。eval:在页面中执行 JavaScript(摄像机可见)。
选项:
- 登录态:
storage指向 Playwright 的存储状态文件(使用npx playwright codegen --save-storage=auth.json <url>保存)。或者使用"cookiesFrom": "agent-browser"借用已登录的 agent-browser 会话。Cookie 不会被打印输出。 - 隐私保护:
keep是在录制前以及录制过程中每 250ms 执行的 JavaScript,用于将真实姓名和邮箱替换为占位内容,确保重新渲染时不会泄露真实数据。hide用于添加 CSS 选择器以隐藏某些元素(Next.js 和 Vercel 的开发覆盖层默认已隐藏;设置"hide": false可显示它们)。 - 摄像头外操作:
setup是在录制前执行一次的 JavaScript(例如关闭弹窗)。 - 视觉效果:
travelMs(光标移动时间,默认 700)、settle(页面加载后等待时间,默认 1500)、tail(录制结束后的保持时间,默认 1200)、cursorStart(光标起始位置[x, y])、reducedMotion: true(减少动效)。 - 加载策略:
waitUntil(默认networkidle)。如果页面持续传输视频或轮询,则永远不会达到空闲状态,捕获会超时;此时应设置为"load",让settle覆盖剩余等待时间。 - 另一台服务器: 在捕获命令中添加
--base http://localhost:3001可在其他地址录制相同路径。
选择器: 优先使用 data-testid 或结构选择器(#title、input[name=amount]),而不是可见文本,这样文本内容变化不会破坏脚本。
渲染参数
--zoom 1.6控制点击时的缩放程度(当两个元素同时重要时使用 1.3)。--speed 0.9使播放速度降低 10%;--speed 2.5用于快速蒙太奇效果。--end 9.8在第 9.8 秒处截断;--hold 1.2冻结最后一帧。--matte '#fdfaf1'将页面以圆角窗口形式置于该颜色背景上(默认全屏显示)。--title 'Moving an event' --kicker 'Events'添加开场标题;--end-cap添加结束卡片。--brand brand.json设置标题和卡片的视觉效果:ink、paper、accent、displayFont/monoFont({ "family", "file", "weight" })、logo、footer、kicker。路径相对于品牌配置文件。--fit按页面原始比例渲染(例如手机视口为竖屏)。--gif同时生成 960px 宽度、15fps 的 GIF 文件。
规则
- 永远不要录制真实客户数据。 使用测试账号,并通过
keep钩子处理任何个人信息;在分享前务必检查联系表。 - 短小精悍,单一用途。 每个片段只展示一个功能点;使用
cut命令裁切等待时间。 - 将视频文件存放在代码仓库之外。 将脚本(以及文档需要使用的海报)提交到仓库;MP4 和 GIF 文件应存放在对象存储或 CDN 中。