第 3 课

渲染 MP4、用变量出第二版,从 Catalog 装一个字幕条组件

渲染 1920×1080 30fps 7 秒 MP4 并用 ffprobe 核对;用 variables 与 strict-variables 参数换文案和配色出第二版,并看到拼错变量名会被拦下;用 catalog 搜索、add 安装 lt-clean-bar 字幕条,以子合成方式挂上时间轴;同机两次渲染 MD5 一致,最后用 clean 预览清理。

图文25 分钟

课程目录第 3 / 3 课

学习位置仅保存在当前浏览器,有效期 180 天。

01 / 图文教材

渲染 MP4、用变量出第二版,从 Catalog 装一个字幕条组件

本课对照的官方页面:CLI 参考中的 render / catalog / add / clean 、变量 、合成与子合成 、确定性渲染 。测试环境同课时 1:Node.js v22.21.1、FFmpeg 7.1.5、hyperframes 0.9.26,8 核 CPU、无独立显卡。所有「对照结果」均为真实输出节选,超长行已折行。

上一课的片头已经通过 check。这一课把它真正渲染成 MP4 并核对参数,再用同一份模板换文案和配色出第二版,最后从官方组件库(Catalog)搜一个字幕条组件,挂进片头再渲染一次。

第 1 步:渲染成 MP4

在 kun-intro 目录里运行:

hyperframes render --output renders/kun-intro.mp4

对照结果(节选):

◆  Rendering kun-intro → …/kun-intro/renders/kun-intro.mp4
   30fps · standard · auto workers (8 cores detected)
   GPU: browser GPU (auto-detect)

Checking browser...
Browser: cache

◇  …/kun-intro/renders/kun-intro.mp4
   210.0 KB · 7.0s video · rendered in 3.9s
   beginframe capture · software gpu · compile 0.2s · setup 1.0s · capture 2.6s · encode
       (during capture) 2.6s · assemble 0.1s

7 秒视频 3.9 秒渲完。Browser: cache 表示用的是已经下载好的 chrome-headless-shell;你第一次渲染时这里会显示 Browser: download,要多等一会儿。不写 --output 时文件会落在 renders/项目名_日期_时间.mp4。

用 FFmpeg 自带的 ffprobe 核对参数:

ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,width,height,r_frame_rate,nb_frames:format=duration \
  -of default=nw=1 renders/kun-intro.mp4

对照结果:

codec_name=h264
width=1920
height=1080
r_frame_rate=30/1
nb_frames=210
duration=7.000000

H.264、1920×1080、30fps、正好 210 帧 7 秒,和根元素上写的尺寸与时长一致。想要别的规格,常用参数有 --fps 60、--quality draft(快速出样片)或 --quality high、--format webm(带透明通道,适合叠加层)、--format gif。

第 2 步:用变量换文案和配色,出第二版

上一课已经在 <html> 上声明了 title、subtitle、accent 三个变量,并用 data-var-text 与 var(--accent) 绑定。渲染时用 --variables 传一个 JSON 覆盖默认值,加上 --strict-variables 让拼错的变量名直接报错:

hyperframes render \
  --variables '{"title":"HyperFrames","subtitle":"写 HTML,渲染成视频","accent":"#f97316"}' \
  --strict-variables --quality draft \
  --output renders/kun-intro-orange.mp4

对照结果(节选):

◆  Rendering kun-intro → …/kun-intro/renders/kun-intro-orange.mp4
   30fps · draft · auto workers (8 cores detected)

◇  …/kun-intro/renders/kun-intro-orange.mp4
   285.4 KB · 7.0s video · rendered in 3.4s

截第 2 秒对比:默认版是「昆仑学堂 / 用 HTML 写一段视频」配绿色进度条,新版变成「HyperFrames / 写 HTML,渲染成视频」配橙色进度条,版式和动画完全不变。

再故意把 title 拼成 titel 试试严格模式:

hyperframes render --variables '{"titel":"typo"}' --strict-variables --output renders/bad.mp4
echo "exit=$?"

对照结果:

Variable issue (1) — values may not render as expected:
  Variable "titel" is not declared in data-composition-variables.

✗  Variable validation failed

   Aborting render due to variable issues (--strict-variables mode).
exit=1

渲染被拦下,退出码为 1,renders/ 里不会多出 bad.mp4。批量出片时一定要加 --strict-variables。要一次出很多版,官方还提供 --variables-file(从文件读)和 --batch(JSON 数组每行出一个文件),用法见 CLI 参考的 render 一节。

第 3 步:从 Catalog 搜一个字幕条组件并装进项目

官方组件库有约 450 个现成的转场、标题、图表、字幕条等组件。先按描述搜:

hyperframes catalog --query "lower third name title"

对照结果(节选):

  40 of 448 moves · local word match
Name                        Type        Description
lt-kicker-name              block       Cardless lower third with an accent eyebrow/kicker
    tag, heavy name, and a drawn baseline; for footage
lt-stack-bars               block       Two stacked bars: a dark name bar wipes from the
    left, an accent role bar wipes from the right
lt-soft-pill                block       Rounded white pill lower third for
    podcasts/interviews: status dot, name + role, scale-pop entrance
…

不带 --query 会列出全部;--type block、--tag social 可以过滤;加 --on-device 会改成语义搜索,但需要先下载一个 33 MB 的本地模型。本课选 lt-clean-bar(白色卡片、左侧强调色竖条的姓名条):

hyperframes add lt-clean-bar --no-clipboard

对照结果:

✓ Added lt-clean-bar (hyperframes:block)
  compositions/lt-clean-bar.html

Include snippet:
  <div data-composition-id="lt-clean-bar"
      data-composition-src="compositions/lt-clean-bar.html" data-duration="4.8"
      data-width="1920" data-height="1080"></div>

Paste the snippet above into your host composition.

组件以源码形式落在 compositions/lt-clean-bar.html,可以随便改。--no-clipboard 表示不把片段写进剪贴板,在服务器和 CI 上要加。

第 4 步:改组件文字,挂进片头

组件里的示例文字是 Dr. Maya Chen 与 Host · Neuroscientist。用编辑器改成你自己的,或者用 sed(macOS 上写成 sed -i ''):

sed -i 's#>Dr. Maya Chen<#>HyperFrames<#; s#>Host · Neuroscientist<#>Write HTML. Render video.<#' \
  compositions/lt-clean-bar.html

然后在 index.html 里网址那一段 <p id="outro" …> 的上面,加上刚才的挂载片段,并补上开始时间和轨道号:

<div id="lower-third"
     data-composition-id="lt-clean-bar"
     data-composition-src="compositions/lt-clean-bar.html"
     data-start="1.5" data-duration="4.8" data-track-index="3"
     data-width="1920" data-height="1080"></div>

data-composition-src 的路径从项目根目录算。子合成自带自己的时间线,父级只决定它何时出现:这里从第 1.5 秒开始,持续 4.8 秒。

hyperframes lint
hyperframes timeline

对照结果:

◆  Linting kun-intro/2 files

◇  0 errors, 0 warnings

timeline 7s

graphics (6: 5 top-level, 1 nested)
  |███████████████████████                 | #title title 0-4s
  |███████████████████████                 | #subtitle subtitle 0-4s
  |███████████████████████                 | #bar bar 0-4s
  |        ████████████████████████████    | #lower-third lower-third 1.5-6.3s
      src=compositions/lt-clean-bar.html
  |        ████████████████████████████    | #lt-clip lt-clip 1.5-6.3s (local 0-4.8s) nested
      in lower-third compositions/lt-clean-bar.html
  |                      ██████████████████| #outro outro 4-7s

lint 这次检查了 2 个文件,时间轴上字幕条落在 1.5 到 6.3 秒,组件内部的本地时间 0 到 4.8 秒被映射过去。先抽第 3 秒确认位置:

hyperframes snapshot --at 3 --no-end --output snaps

对照结果(节选):

◆  Capturing 1 frames at [3s] from kun-intro
   Fonts: 10 loaded
[hyperframes] snapshot: frame 1/1 at 3s
◇  1 snapshots saved to …/kun-intro/snaps

Fonts: 10 loaded 比上一课的 2 个多,多出来的是组件用的 Montserrat 字体:测试机第一次检查带这个组件的项目时,日志显示从 Google Fonts 拉取了 Montserrat 并缓存到 ~/.cache/hyperframes/fonts/montserrat。打开 snaps/frame-00-at-3s.png,左下角出现白色卡片「HyperFrames / Write HTML. Render video.」,中间仍是「昆仑学堂」标题。

第 5 步:最终渲染,并验证可复现

hyperframes render --output renders/kun-intro-lt.mp4
hyperframes render --output renders/kun-intro-lt-2.mp4
md5sum renders/kun-intro-lt.mp4 renders/kun-intro-lt-2.mp4

对照结果(第二次渲染输出略):

◇  …/kun-intro/renders/kun-intro-lt.mp4
   313.9 KB · 7.0s video · rendered in 5.4s

e47b1390fdf733408ee61f19c55c0e6c  renders/kun-intro-lt.mp4
e47b1390fdf733408ee61f19c55c0e6c  renders/kun-intro-lt-2.mp4

同一台机器连续渲染两次,文件逐字节相同。这就是「逐帧跳转再截图」带来的好处:不受渲染时机器忙不忙的影响。官方确定性渲染文档也提醒,不同电脑的字体和 Chrome 版本不同,本机渲染换一台机器可能差一个像素,需要跨机器逐字节一致时用 hyperframes render --docker(需本机装 Docker,本课测试机未装,未实测)。

最后清理一下渲染留下的临时文件,先看会删什么:

hyperframes clean --dry-run

本课测试机的结果是 Nothing to clean,只列出了可以用 --snapshots 额外回收的快照与 check 报告(约 200 KB)。渲染出的 MP4、源码、浏览器和模型永远不会被 clean 删除。

常见坑

  • 字幕条整块不见:多半是你给通用的 .clip 类写了定位样式,组件内部也用 .clip,被一起改了位置。把布局样式写到具体 id 上
  • 组件的 Montserrat 来自 Google Fonts,网络不通时会退回默认字体;可以像上一课那样把字体文件放进项目并在组件里写 @font-face
  • --variables 的 JSON 在 Windows PowerShell 里要注意引号转义,最稳的是写进文件后用 --variables-file
  • 想让每次渲染都一样,动画里不要用 Date.now()、requestAnimationFrame 和不带种子的 Math.random(),也不要在渲染中途再去请求网络资源(官方确定性渲染文档列出的规则)
  • --quality draft 只适合看样片;交付用默认的 standard 或 --quality high
  • 内存 8 GB 及以下的机器会自动进入低内存模式(单进程、较慢),属正常现象;也可以用 --workers 1 手动限制

小结

你把片头渲染成了 1920×1080、30fps、7 秒的 H.264 视频,并用 ffprobe 核对;用 --variables 与 --strict-variables 一份模板出了第二版,并看到拼错变量名会被拦下;用 catalog 与 add 把官方字幕条组件装进项目,以子合成的方式挂上时间轴;最后验证了同机两次渲染 MD5 一致。接下来可以运行 npx hyperframes skills update,让 Claude Code、Codex 或 Cursor 在同一个项目里按 AGENTS.md 的约定继续帮你改。