变量
变量允许你在合成中声明命名的、带类型的槽位,并在渲染时填充它们——从父合成、CLI 或 API 调用中获取。一个接受 title 和 color 的卡片合才可以被嵌入一百次,每次使用不同的值,而无需重复任何 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"}]}
]'>
每个声明需要四个字段:id、type、label 和 default。id 在合成内必须唯一。
变量类型
| 类型 | default 值 | 附加选项 |
|---|---|---|
string | "some text" | placeholder?: string, maxLength?: number |
number | 0 | min?: number, max?: number, step?: number, unit?: string |
color | "#rrggbb" | — |
boolean | true / false | — |
enum | 选项值之一 | options: [{value: string, label: string}] |
Studio 编辑界面使用 label、type 和特定类型的选项来为每个变量渲染正确的输入控件。
什么可以成为变量
变量分为两个层次。上面的五种声明类型涵盖了带类型的原始数据——字符串、数字、颜色、布尔值、枚举。对于其他所有情况,持有 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-start、data-duration、data-track-index、data-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、缺少的必填字段(id、type、label、default)以及 type 与 default 值之间的类型不匹配。在渲染前修复 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 相同。