核心概念

变量

为合成添加参数化,使同一源文件可以渲染不同的内容。

变量允许你在合成中声明命名的、带类型的槽位,并在渲染时填充它们——从父合成、CLI 或 API 调用中获取。一个接受 titlecolor 的卡片合才可以被嵌入一百次,每次使用不同的值,而无需重复任何 HTML。

声明变量

在任何合成的 <html> 根元素上添加 data-composition-variables。它的值是一个变量声明的 JSON 数组——每个变量一个对象:

<html data-composition-variables='[
{"id":"title",  "type":"string",  "label":"Title",  "default":"Hello"},
{"id":"color",  "type":"color",   "label":"Color",  "default":"#111827"},
{"id":"price",  "type":"number",  "label":"Price",  "default":0,        "unit":"$"},
{"id":"featured","type":"boolean","label":"Featured","default":false},
{"id":"plan",   "type":"enum",    "label":"Plan",   "default":"pro",
"options":[{"value":"pro","label":"Pro"},{"value":"enterprise","label":"Enterprise"}]}
]'>

每个声明需要四个字段:idtypelabeldefaultid 在合成内必须唯一。

变量类型

类型default附加选项
string"some text"placeholder?: string, maxLength?: number
number0min?: number, max?: number, step?: number, unit?: string
color"#rrggbb"
booleantrue / false
enum选项值之一options: [{value: string, label: string}]

Studio 编辑界面使用 labeltype 和特定类型的选项来为每个变量渲染正确的输入控件。

什么可以成为变量

变量分为两个层次。上面的五种声明类型涵盖了带类型的原始数据——字符串、数字、颜色、布尔值、枚举。对于其他所有情况,持有 URL 的 string 变量是万能的解决方案:你的合成读取 URL 并将其赋给需要它的任何 DOM 元素。

参数化媒体资源

同一个合成可以通过字符串变量替换 URL 来渲染不同的图片、视频片段或音频轨道:

<html data-composition-variables='[
{"id":"productImage","type":"string","label":"Product image URL","default":"https://cdn.example.com/products/default.png"},
{"id":"productName","type":"string","label":"Product name","default":"Untitled"}
]'>
<body>
<div data-composition-id="product-card" data-width="1920" data-height="1080" data-duration="5">
<img class="product-img" alt="" />
<h1 class="product-name"></h1>

<script>
const {
productImage = "https://cdn.example.com/products/default.png",
productName = "Untitled",
} = __hyperframes.getVariables();
const root = document.querySelector('[data-composition-id="product-card"]');
root.querySelector(".product-img").src = productImage;
root.querySelector(".product-name").textContent = productName;
</script>
</div>
</body>
</html>

ℹ️ Note

运行时会在合成脚本执行后探测 DOM,因此从变量赋值的 <video><audio>src 会被发现并为渲染进行预提取。无需额外配置——只需从变量设置 src 即可。

同样的模式适用于三种媒体元素类型:

  • <img src> -- 从字符串变量赋值。Chrome 在捕获时像处理任何其他图片一样获取它;无需额外配置。
  • <video src> -- 从字符串变量赋值,但将时间属性(data-startdata-durationdata-track-indexdata-has-audio)保留在元素本身上。探测阶段在脚本执行后扫描 video[data-start] 元素,并读取解析后的 src 进行预提取。
  • <audio src> -- 与视频相同。音频在捕获时被解码并混合到最终输出中。

请以 URL 引用的形式传递资源,由合成在渲染时解析;不要内联 base64。URL 格式的资源可以干净地通过本地渲染器和 Lambda 环境传输——关于分布式渲染的 256 KiB 执行输入上限,请参阅 Templates on Lambda

替换媒体:是否也需要调整持续时间?

一个常见的后续问题:如果变量将 <video> 替换为不同的片段,data-duration 是否也需要改变?通常不需要。data-duration<video><audio> 上是可选的——如果不设置,渲染器会使用 ffprobe 获取源文件的自然长度:

<video id="hero" data-start="0" data-track-index="0"></video>
<script>
document.getElementById("hero").src = __hyperframes.getVariables().heroVideo;
</script>

如果你需要将片段限制或固定到每次渲染的特定长度——例如,在不同源长度的片段之间保持下游时间的稳定性——可以将持续时间暴露为独立的 number 变量,并通过相同的脚本应用:

<video id="hero" data-start="0" data-track-index="0"></video>
<script>
const { heroVideo, heroDuration } = __hyperframes.getVariables();
const el = document.getElementById("hero");
el.src = heroVideo;
if (heroDuration !== undefined) {
el.setAttribute("data-duration", String(heroDuration));
}
</script>

探测阶段在脚本执行后从活动 DOM 读取 data-duration,因此通过编程方式设置的属性与嵌入在源 HTML 中的属性行为完全相同。

什么不能成为变量

一小部分输入仅从源 HTML 或 CLI / SDK 读取一次,不会从活动 DOM 重新读取——没有脚本(因此也没有变量)可以改变它们:

内容机制(非变量)
合成尺寸合成元素上的 data-width / data-height —— 在编译时从源 HTML 解析,而非从活动 DOM 读取
帧率hyperframes render--fps 标志,或 SDK 中的 config.fps
输出格式 / 编解码器 / 质量--format / --codec / --quality 标志,或 SDK 等效项
兄弟或父合成的变量变量是按合成隔离的;在每个子合成宿主元素上使用 data-variable-values 传递覆盖值

更深层的规则:变量是你的脚本应用到 DOM 的运行时值。它们可以驱动渲染器在脚本执行后从活动 DOM 读取的任何内容——文本、颜色、媒体 src,甚至如上所示的片段 data-duration。它们无法改变渲染器在编译时只读取一次的输入(尺寸)或完全存在于合成外部的内容(CLI 标志、编码器设置)。

在运行时读取变量

在任何合成脚本内部,调用 window.__hyperframes.getVariables() 获取解析后的变量值。返回类型为 Partial<Record<string, unknown>>——使用解构赋值并提供与声明的 default 值匹配的默认值:

<html data-composition-variables='[
{"id":"title","type":"string","label":"Title","default":"Untitled"},
{"id":"color","type":"color","label":"Color","default":"#111827"}
]'>
<body>
<div data-composition-id="card" data-width="1920" data-height="1080">
<h1 class="card-title"></h1>

<style>
[data-composition-id="card"] { --card-color: #111827; }
[data-composition-id="card"] .card-title { color: var(--card-color); }
</style>

<script>
const { title = "Untitled", color = "#111827" } = __hyperframes.getVariables();
const root = document.querySelector('[data-composition-id="card"]');
root.querySelector(".card-title").textContent = title;
root.style.setProperty("--card-color", color);
</script>
</div>
</body>
</html>

__hyperframes.getVariables()window.__hyperframes.getVariables() 的简写,在顶层和子合成脚本中都可用。运行时会自动隔离子合成的作用域,使每个实例只看到自己的解析值。

逐实例覆盖(子合成)

当将一个合成嵌入另一个合成时,在宿主元素上使用 data-variable-values 为该特定实例传递覆盖值的 JSON 对象:

<div
data-composition-id="card-pro"
data-composition-src="compositions/card.html"
data-start="0"
data-track-index="1"
data-variable-values='{"title":"Pro","color":"#ff4d4f"}'
></div>
<div
data-composition-id="card-enterprise"
data-composition-src="compositions/card.html"
data-start="card-pro"
data-track-index="1"
data-variable-values='{"title":"Enterprise","color":"#22c55e"}'
></div>

两个宿主元素指向同一个 card.html 源文件,但每个实例接收不同的值。运行时按实例将宿主的 data-variable-values 合并到子合成声明的默认值之上——同一个子合成可以同时以完全不同的内容运行。

CLI 覆盖(顶层渲染)

在渲染时使用 --variables--variables-file 传递变量值。这些会覆盖顶层合成声明的默认值:

# 内联 JSON
npx hyperframes render --variables '{"title":"Q4 Report","color":"#1d4ed8"}' --output q4.mp4

# JSON 文件
npx hyperframes render --variables-file ./vars.json --output out.mp4

# 对未声明或类型不匹配的变量报错
npx hyperframes render --variables '{"title":"Q4 Report"}' --strict-variables --output out.mp4

--strict-variables 将变量警告变为错误。--variables 中任何在 data-composition-variables 中未声明的变量,或其值与声明类型不匹配的变量,都会导致渲染以非零状态退出。这在 CI 流程中很有用,因为未声明的变量键通常表示拼写错误或 schema 不匹配。

ℹ️ Note

CLI 覆盖仅应用于顶层合成。子合成变量由每个宿主元素上的 data-variable-values 控制。

分层和优先级

变量值通过合并三个来源来解析,从低到高优先级:

来源优先级声明位置
声明的默认值最低<html> 上的 data-composition-variables
逐实例宿主覆盖中间子合成宿主元素上的 data-variable-values
CLI --variables 标志最高hyperframes render --variables '{...}'

任何层级缺失的键会向下传递到下一层级。如果没有层级提供值,则使用声明的 default

验证

Lint 工具静态检查变量声明:

npx hyperframes lint

它会捕获格式错误的 JSON、缺少的必填字段(idtypelabeldefault)以及 typedefault 值之间的类型不匹配。在渲染前修复 lint 错误——它们表明运行时将无法正确解析变量。

在渲染时,CLI 会根据 schema 验证 --variables,并将问题报告为警告(或在 --strict-variables 模式下报告为错误):

  • undeclared -- --variables 中的键在 data-composition-variables 中没有匹配的 id
  • type-mismatch -- 值的 JavaScript 类型与声明的 type 不匹配(例如期望数字却得到了字符串)
  • enum-out-of-range -- 枚举值不在声明的 options 列表中

编程式检查变量

如果你正在基于 @hyperframes/core 构建工具链,无需渲染即可读取变量声明:

import { extractCompositionMetadata } from "@hyperframes/core";
import { readFileSync } from "node:fs";

const html = readFileSync("compositions/card.html", "utf8");
const { variables } = extractCompositionMetadata(html);
// variables 是 CompositionVariable[]

这与 Studio 编辑界面用来为每个合成构建变量面板的 API 相同。

下一步

  • 数据属性 data-composition-variables 和 data-variable-values 属性的完整参考
  • 合成 嵌套合成如何使用变量实现复用
  • 渲染 在渲染时传递变量的 CLI 标志
  • CLI 参考 所有 CLI 命令和标志