关于 site-reels
site-reels 用于录制本地运行网站中的脚本化场景,并生成视频素材或展示片,面向制作网站与产品视频的团队。它提供浏览器录制和确定性逐帧捕获两种流程。
主要功能
- 01脚本化浏览器场景录制
- 02可配置尺寸的 MP4 输出
- 03通过 snap.mjs 控制时间逐帧捕获
- 04场景准备与交互钩子
- 05Remotion 展示片合成指引
- 06视频与帧检查流程
适用场景
- 01录制网站品牌场景
- 02捕获产品操作流程制作展示片
- 03制作确定性 60fps 视频素材
- 04为 Figma Slides 准备视频封面
MCP 相关
README 提及使用 Figma MCP 工具(如 use_figma 和 upload_assets)创建幻灯片占位内容并上传封面图片;同时说明 MP4 不通过 MCP 上传。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pedrojaques99/ visant-skills-public --skill site-reels安装后在 Claude Code 中输入/site-reels确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cases.mdscripts/deliver.example.shfind-chrome.mjsloop.shpatch-timeweb.mjsrecord.mjsscenes.example.mjssnap.mjs详细介绍
Site Reels:网站 → MP4 → 演示文稿
要求
| 适用于 | 要求 | 检查 |
|---|---|---|
| 所有功能 | 配备 GPU 和可见桌面的 Windows、macOS 或 Linux(Chrome 以有头模式运行) | 不适用 |
| 所有功能 | Node.js 18+ | node -v |
| 所有功能 | PATH 中有 ffmpeg + ffprobe(或 FFMPEG=<full path>) | ffmpeg -version |
| 所有功能 | Chrome、Chromium 或 Edge(自动检测,见下文) | node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))" |
record.mjs | puppeteer-core + puppeteer-screen-recorder(npm,位于项目中) | npm ls puppeteer-core puppeteer-screen-recorder |
snap.mjs | puppeteer-core + timesnap(会引入 timeweb) | npm ls timesnap |
| 展示视频 | Remotion 项目 | npx remotion --version |
| 审核(可选) | ImageMagick | magick -version |
| Figma 上传 | Figma MCP(use_figma、upload_assets)+ 技能 figma-use、figma-use-slides | MCP 工具列表 |
Chrome 检测(scripts/find-chrome.mjs,由 record.mjs 和 snap.mjs 使用):如果设置了 CHROME
环境变量,则优先使用;否则查找已安装的 Chrome/Edge(Windows:Program Files 和 LocalAppData;macOS:
/Applications 和 ~/Applications;Linux:PATH 中的 google-chrome、chromium、microsoft-edge);
否则使用最新的 ms-playwright/chromium-<N>(或 PLAYWRIGHT_BROWSERS_PATH);否则停止并
告知如何设置 CHROME 或运行 npx playwright install chromium。在 Windows 上会添加
--use-angle=d3d11;在其他操作系统上,Chrome 会保留默认的 GPU 后端。
功能
录制在本地运行的网站中的脚本化场景,并生成可直接用于 Figma 的 MP4(1920×1080、 30fps、x264、< 100MB)。已验证的引擎:puppeteer-core + puppeteer-screen-recorder(CDP 屏幕录制 + ffmpeg)。切勿录制屏幕。
运行
mkdir <proj>-reels && cd <proj>-reels && npm init -y && npm i puppeteer-core puppeteer-screen-recorder
mkdir -p scripts && cp ~/.claude/skills/site-reels/scripts/{record,find-chrome}.mjs scripts/
cp ~/.claude/skills/site-reels/scripts/scenes.example.mjs scenes.mjs # edit the scenes
# in the site repo: npx next build && npx next start -p 3100 (production build: smoother WebGL)
OUT="<output folder>/Videos" node scripts/record.mjs scenes.mjs [scene ...]
运行复制到项目中的脚本:ESM 会从脚本所在文件夹解析 puppeteer-core,而不是从当前工作目录解析。
环境变量:BASE(默认值 http://localhost:3100)、OUT、CHROME、FFMPEG、W、H、DPR、
MOBILE=1(触摸模拟)、KEEP_UI=1(保留导航栏)、MIN_FPS(24)。手机 4:5:
W=360 H=450 DPR=2 MOBILE=1 会生成 720x900。
每个场景 = { name, path, theme?, trim?, crf?, css?, prepare(page, ctx), act(page, ctx) }。
prepare 在录制前运行(滚动到指定部分,等待 3D 加载);act 中的内容会录入视频。ctx 包含
wait、scrollTo(page, y|selector, ms) 和
scrollThrough(page, selector|null, ms, from, to),并带有缓动效果。加载时播放的片头:
在 act 中执行 page.reload(),并使用 trim 剪掉加载过程。
适用的品牌脚本:Logo/主视觉片头(浅色和深色)、主视觉媒体切换、随滚动让镜头进入的 3D 部分、沉浸式路线(漫游)、页脚文字标识以及一次完整的全页浏览。 5–7 个场景,每个 12–55 秒。
交付前审核(不要相信退出码)
- 每个视频生成联系表:
ffmpeg -i x.mp4 -vf "fps=6/<dur>,scale=480:-1,tile=3x2" -frames:v 1 sheet.png,并查看。 - 19 秒视频只有 1MB = 空白场景。> 100MB = 无法导入 Figma(场景的
crf可修复;有颗粒效果 + 拖影的场景设为 24)。 - 在多个时间点检查顶部条带(
crop=1920:120:0:0),捕捉意外露出的导航栏。 - 带有开头/结尾长暂停的
LOW-FPS是误报(检测器会将静止帧计入);请检查联系表。
已知问题
- 隐藏窗口会冻结 rAF → 帧冻结。启动时已传入
--disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusion。 特意设置headless: false:无头模式会回退到 SwiftShader,导致 WebGL 卡顿。 - 隐藏 UI 的 CSS 会消失,页面重新加载时以及 React hydration 清空
<head>时都会如此 → 脚本每 100ms 将<style id="reels-hide">重新附加到 body。 - 一个无效选择器会导致整条规则失效:JS 模板字面量中的
\[会变成[,而.z-[60]会使header, nav, .z-[60]失效。每个风险选择器单独写一条规则。 - 不要隐藏
[data-cursor]:网站会给 3D 画布和轮播图添加此属性。应定位自定义光标元素(fixed + mix-blend + pointer-events-none)。 - 延迟加载的 3D 内容(IntersectionObserver + dynamic import):到达相应区域后等待 5–7 秒。没有
preserveDrawingBuffer时,读取 WebGL 画布会得到空内容:使用固定等待时间或截图。 - 屏幕录制每秒约输出 32 个不重复帧 → 以 30fps 录制(60fps 会产生不规则的重复帧)。默认的
trim0.3s 会丢弃首个缓存帧。 - 机器负载较高时,屏幕录制会卡顿(Photoshop 开启、并行渲染):MP4 显示为“30fps”,但每秒只有 9–18 个不重复帧。
record.mjs会进行测量(mpdecimate=hi=64:lo=24:frac=0.05),若低于MIN_FPS(24)则最多重新录制 2 次。对于 WebGL/3D 片段,优先使用snap.mjs。 - Cookie 横幅:录制前在
prepare中接受 Cookie(通用方式点击“Accept/Aceitar/OK”)。 - 不需要滚动的工具页面(生成器、效果编辑器)录制出来会是静止画面:场景必须有交互(向
input[type=file]上传文件、按按钮文本点击预设)。自定义滑块不会响应合成的input事件。
60fps 锁定片段:snap.mjs(确定性)
scripts/snap.mjs 注入 timeweb(timesnap 的虚拟时钟:rAF、performance.now、
setTimeout 和 video),每帧前进 1/60s 并截图,不受机器负载影响。
场景配置:{ name, path, theme, css, dur, wait (等待 3D 加载的实际毫秒数), pre (滚动前的虚拟毫秒数,2000;对于加载时即播放的片头,设为 **0**), start (跳过的虚拟秒数), scroll: { sel, from, to, at, len } | { bottom: [from, to] in screen heights }, frame(page, t) (例如鼠标) }。滚动由虚拟时间驱动。
npm i puppeteer-core timesnap && mkdir -p scripts
cp ~/.claude/skills/site-reels/scripts/{snap,find-chrome,patch-timeweb}.mjs scripts/
npm pkg set scripts.postinstall="node scripts/patch-timeweb.mjs" && npm run postinstall
FPS=60 OUT=... node scripts/snap.mjs scenes-snap.mjs [scene]
- 不要直接使用
timesnap():使用 Next + R3F + Lenis 时,它录下的是空白页面,WebGL 主视觉区域也有一段段冻结画面(rAF 不会运行)。自定义循环(goTo + scrollTo + screenshot)在所有场景中都有效:60 帧中有 60 个不重复帧。 - 必须应用 timeweb 补丁:它会将
video.play()替换为不返回 Promise 且不触发playing事件的版本 →video.play().catch()会抛出错误,导致页面变成空白。 - 在 getContext 上强制启用
preserveDrawingBuffer: true(使用preserve: false可关闭)。 - 帧保存到操作系统临时目录,绝不保存到同步驱动器(数千张 jpg 会触发同步)。
- 不要并行执行 GPU 任务(Remotion 渲染、Photoshop):WebGL 会丢失其上下文,录下数秒的纯背景画面。脚本会发出
FLASH警告(帧亮度闪烁);较长的空白片段只有在联系表中才会显现。请检查。 - 真实的流畅度审核:以完整分辨率使用
magick compare -metric MAE比较相邻帧。0 = 重复帧。 - 交付:
scripts/deliver.example.sh(复制并重命名片段;必须设置V=)会构建显现效果、循环(loop.sh)和干净的 3D 内容,并将 crf 每次提高 3,直到文件小于 95MB。
上传到 Figma Slides(隐藏起来,不打扰团队)
MP4 不能通过 MCP 上传:use_figma 无网络,而且 Plugin API 无法通过字节数据创建视频;upload_assets 只接受图片(≤ 10MB)。因此:
- 提取每个视频的封面:
ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg。 - 使用
use_figma(技能figma-use+figma-use-slides):在最后一张幻灯片前执行createSlideRow(idx),设置row.name = 'WIP · Motion (hidden)';每个视频执行一次createSlide(idx, i),并设置isSkippedSlide = true、黑色背景、一个命名为VIDEO · x.mp4的 1920×1080 矩形,以及写入speakerNotes的说明。 - 将矩形的
nodeIds传给upload_assets→ 通过 multipart POST 上传封面。 - 用户将 MP4 拖到矩形上(用视频替换封面)。请明确说明这一点;绝不要承诺视频已插入。
getSlideGrid() 会省略已跳过的幻灯片:通过 SLIDE_GRID.children[i].children 检查结构。
品牌展示视频(Remotion):素材底片 + 设计场景
制作品牌展示视频(logo、颜色、字体、布局、宣言、样机)时,只从网站录制干净的素材底片(不含文字:每个场景的 CSS 会隐藏标题/导航),其余部分用 Remotion 合成(逐帧渲染,无抖动)。值得在 src/fx.tsx 中准备可复用的构件:Mist、MistWords、Grain、KenBurns、Plate、nevoa()(雾气)转场,并将品牌数据放在 brand.ts 中。
- 视觉识别来自演示文稿,而非网站:在幻灯片中确认官方字体和调色板(网站可能偏离品牌手册)。
- 可行的场景:雾中开场短句、录制揭示、logo 构建(
evolvePath+ 根据 logo 栅格测量的辅助线)、逐句呈现宣言、使用可变wght轴的支柱、色带调色板、排版、分层图形、带有mix-blend-difference标题的 3D、滚动布局列、1/2/3 屏样机蒙太奇、与名称同步的产品展示、以字标收尾。 - 渲染前通过静帧审阅(
npx remotion still ... --frame=N+ 蒙太奇)。 - 使用
--concurrency=3渲染:在 Photoshop 开启时,32GB 内存的机器使用并发数 8 会耗尽内存。 - 循环视频:
scripts/loop.sh in out [start] [dur] [crossfade]。使用magick compare -metric RMSE比较首尾帧,检查衔接处(< 0.5% = 不可见)。 - 不合格的素材底片应直接舍弃:旋转的旋转体看不出运动;为竖屏画布构图的镜头放在 16:9 画面中会显得很小。
- 急促风格的视频(获批节奏):25–35s、60fps,使用
Series,按 120 BPM 网格(30 帧)进行硬切;每 3–6 帧出现一组图像;网格上叠加巨幅 logo 裁切图、闪变的字体粗细、重新排列的色块、圆形蒙版中的肖像、图形爆发、动态短句、持续一拍的样机镜头,并以停留约 2.5s 的 logo 收尾。零标签:文字只能作为图形出现。节奏缓慢的 1:40 展示视频未获通过。 - 柔和/烟雾风格的视频:录制主视觉烟雾时不要出现 logo 或照片,使用纯白背景上的黑色烟雾(带
storage= localStorage 中的参数,以及block: ['assets/logo.png']的场景;空白 PNG 必须透明;不要屏蔽视频:视频出错会导致 timeweb 卡住)。将素材底片用作mix-blend-mode: multiply叠加层:场景开始时淡入,倒放版本(ffmpeg -vf reverse)在结尾淡出;切点落在黑帧上。约 1.1s 时转场(速率 2),否则半个场景会变黑。 - 拼装 Figma Slides 布局:克隆幻灯片(设为跳过),将每张卡片各复制 3 次,每份只显示背景 / logo / 文字;使用
download_assets下载每个节点(缩放 2 倍),然后删除克隆。Slides 不会导出透明度:将幻灯片设为黑色和白色,分别导出 logo/文字,再通过差值提取 alpha(ImageMagick)。绝不要通过use_figma返回 base64(会撑爆上下文)。
产品展示视频(真实应用实拍)
使用 Playwright + CDP screencast 以 2 倍速真实录制屏幕,步骤为
goto/move/click/type/scroll/wait,指针轨迹保存为 <id>.cursor.json,然后在其上方用 Remotion 重绘光标。
已否决,请勿重蹈覆辙:
- 将设计好的视频片段拼接到真实屏幕画面上(插画开场 + 实拍中段 + 绘制的 CTA): 两种语言混用,角色消失后又出现,幕间的黑色卡片打断节奏。 每个视频只用一种语言。
- 缩小并加速的产品屏幕画面放在 16:9 窗口中:动态信息流和排名变成难以辨认的一片。
- 裁切屏幕的缩放画面和叠加在视频上的文字(巨大的动词、01/07 计数器、 渐变):喧宾夺主。
- 屏幕上空等(“等待中……”):直接切入操作。
- 文案含糊:必须清楚易懂,使用受众日常说的话。
有效的形式:每项操作先单独显示文字,居中且简短(2 拍),然后显示完整屏幕,不裁切,置于干净背景上的居中窗口中,窗口保持录制画面的宽高比。 文字低调(字重 700,逐词入场并带轻微模糊),不加计数器,不加渐变。按音乐轨道网格切换(14 帧 = 128.57 BPM)。最长 40 秒。
实践证明有效的技巧:
- 加速录制画面:使用
<OffthreadVideo trimBefore={from} playbackRate={rate}>,并将点击帧重新计算为/ rate(声音和光标圆环也要同步)。 - 从中途开始另一个合成:
<Sequence from={X}><Sequence from={-start}><Comp/></Sequence></Sequence>。 检查该片段的第一帧:共享元素“飞入”时会显得位置松散。跳过几帧,并重新放置落在前面的冲击音效。 - 每个合成使用不同语言,并通过模块级时间线控制:由各自的入口文件设置一个
globalThis标记,并在 Root 之前导入该入口文件。 - 切换画面之间保持音乐连续:在音乐底轨上使用
trimBefore,使其从上一段停止的位置继续。 - 响度:渲染后,用 2 遍
loudnorm测量并标准化(-14 LUFS / -1 dBTP),同时复制视频。只在最后测量:Remotion 的原始混音会超过 0 dBFS。 - 审核:为每次渲染生成带时间戳的联系表(
fps=2,drawtext=%{pts\:hms},tile=8x6),并在每个片段交界处生成密集联系表(fps=6)。 - 没有 git、且另一个会话正在同时编辑的项目:在新文件中制作新剪辑;编辑共享文件前先备份为
*.bak-*,并告知用户。
作品集卡片和工作室展示视频
- 使用
KEEP_UI=1运行record.mjs(卡片显示网站的实际样子),并使用上面的手机预设。 - 根据清单文件生成场景(仅包含公开且有可用 URL 的项目);没有可用 URL 时,对截图应用 Ken Burns 效果。
- 卡片交付包:首尾交叉淡入淡出循环、2 遍编码以满足 KB 上限、同时提供 VP9 WebM,以及第 1 帧的 WebP 海报图。
- 用于网页的透明 Logo:
--codec=vp9 --pixel-format=yuva420p(Safari 无法播放)+ 用于编辑的 ProRes 4444;Config.setVideoImageFormat("png")可保留透明度。 - Remotion 中的剪纸/定格动画效果:时间按 2 帧量化(24 fps,每 2 帧一个姿势);每个姿势应用 SVG 滤镜(边缘使用 feTurbulence + feDisplacementMap、奶油色边框使用 feMorphology、硬偏移阴影),并循环使用 3 个种子(“boil”);纸张纹理设为正片叠底。通过 DOMParser 按 id 读取设计师 SVG 中的吉祥物部件,因此更换品牌版本时无需改代码。
- CC0 音乐:Free Music Archive(CC0 专辑)。可选用 Python 中的
librosa.beat.beat_track+ 分段 RMS 分析节奏和最饱满的片段。
项目起源故事、参考项目和特定机器的技巧:references/cases.md。