OpenMontage HyperFrames 全屏运动模式指南:用共享背景层 + 透明时间层编排跨场景视觉连续性
导读
本文解读 HyperFrames 组合件创作中最容易被误用的一类需求——全屏帧运动(贯穿多镜头的连续背景、色彩冲洗、跨 Clip 的全出血视觉状态)在 HTML/CSS/GSAP 组合件中的正确写法。它是 HyperFrames Core 组合契约的一个关键模式,在 OpenMontage 中用于 render_runtime = "hyperframes" 的 HTML 渲染路径(产品宣传片、动效字幕、数据可视化等)。读完本文,你将掌握「共享背景层 + 透明定时内容层」的骨架、它为何能规避逐帧重绘与跨场景连续性造假,以及五条硬性规则与对应的 CLI 验证流程(lint/validate/inspect/snapshot)。
一、先回到契约:HyperFrames 如何从 HTML 渲染视频
在深入本模式前,先明确它所在的环境。HyperFrames 将一个 HTML 文件视为一段可渲染的组合件:DOM 用 data-* 属性声明时间结构,动画运行时(默认 GSAP)是可 seek 的,媒体播放由框架接管。一份最小组合件必须包含(详见 minimal-composition.md):
- 根元素
<div>:data-composition-id、data-width、data-height、data-duration; - 至少一个 Clip(任何带
data-start/data-duration/data-track-index的元素); - 一个
paused: true的 GSAP 时间线,同步注册到window.__timelines["<composition-id>"]。
其中与全屏运动直接相关的契约点有(完整属性表见 data-attributes.md):
| 属性 | 含义 |
|---|---|
data-start / data-duration |
Clip 可见时间窗,单位秒。可见区间两端均闭合:start ≤ t ≤ start + duration,因此最后一帧会保持动画解析后的结束态 |
data-track-index |
轨道是「时间重叠」概念而非视觉堆叠概念;视觉前后关系由 CSS z-index 决定 |
class="clip" |
可见定时元素(<div>、<img> 等)的必选运行时可见性标记,缺失时元素将全程可见,无视 data-start/data-duration |
而确定性渲染要求(见 determinism-rules.md)规定:渲染器对每一帧都是「新的一次 seek」,不存在播放累积状态,因此不能依赖时钟、随机数、网络或输入事件,只能动视觉属性白名单(opacity、x、y、scale、rotation、color、backgroundColor、borderRadius、变换),且永远不要动画 display/visibility。这些约束正是下面全屏运动模式赖以成立的地基。
二、为什么:堆叠不透明场景的代价
最容易想到的「多场景视频」做法,是给每个场景一个不透明的、铺满画布的 <section>,让它们按时间先后叠加。对视觉上彻底不连续的场景(硬切)这可行,但只要场景之间存在视觉连续性或「全局」视觉状态,堆叠不透明方案就会暴露出三层代价:
- 每次场景切换都要整帧重绘。 不透明场景互相遮挡,换场意味着整个像素面的重建。
- 跨场景的视觉连续性只能靠「造假」模拟。 例如上一场是深蓝、下一场要承接微妙的色相偏移,你不得不在两个场景里分别硬编码背景色,再祈祷它们衔接得刚好。
- 所有「全局状态」都要在每个场景上复制一份。 一次色相偏移(hue shift)、一个暗角(vignette)、一层胶片颗粒(film grain),都必须在每个场景各自叠一遍——你改一处全局效果,就得改动 N 个场景。
而 HyperFrames 的根元素带固定像素尺寸(如 1920×1080)与 overflow 控制,天然适合把「画面底色」抽离成单一共享层。Full-Screen Motion Pattern 的思路因此非常直白:共享背景层(由可 seek 的时间线驱动)+ 透明的定时内容层。共享层提供一块连续视觉表面,场景则变得轻量、透明——场景换场时,背景不会闪断、不需要「补」连续性,全局状态也只存在于一处。
补充一个来自 composition-patterns.md 与 hyperframes-core/SKILL.md 的实现层佐证:全屏场景填充必须放在一个全出血的
position:absolute; inset:0子元素上,绝不要放在组合件根元素自身的background上——生产器做帧合成时可能丢弃根元素自身的背景导致帧发黑,即便preview/snapshot看起来正常。共享背景层这条规则正好满足「全出血子元素」的要求。
三、模式骨架:共享背景 + 透明时间层
完整示例(源自 full-screen-motion.md 并补充注释):
<div id="root" data-composition-id="main" data-width="1920" data-height="1080" data-duration="20">
<!-- 共享背景 —— 不是 Clip,始终可见,由时间线驱动。 -->
<div id="bg" class="full-bleed"></div>
<!-- 定时内容层 —— 透明背景。 -->
<section
id="scene1"
class="clip transparent"
data-start="0"
data-duration="6"
data-track-index="1"
>
<!-- content -->
</section>
<section
id="scene2"
class="clip transparent"
data-start="6"
data-duration="14"
data-track-index="1"
>
<!-- content -->
</section>
</div>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// 用可 seek 的时间线驱动共享背景。
tl.to("#bg", { backgroundColor: "#0a1530", duration: 6, ease: "sine.inOut" }, 0);
tl.to("#bg", { backgroundColor: "#1a0a30", duration: 14, ease: "sine.inOut" }, 6);
// 场景内动画透明地叠加在上方。
tl.from("#scene1 h1", { y: 48, opacity: 0, duration: 0.6 }, 0.2);
window.__timelines["main"] = tl;
</script>
结构拆解
根元素:data-composition-id="main" 必须与 window.__timelines["main"] 键一致;data-width/height 声明固定像素帧(常见 1920×1080、1080×1920、1080×1080);data-duration="20" 是渲染时长而非时间线长度。根元素 CSS 应带明确像素尺寸并 overflow: hidden(除非故意在画布外构图)。
共享背景 #bg:一个 .full-bleed 的绝对定位铺满子元素(position:absolute; inset:0)。它不是 Clip——不带 data-start/data-duration/data-track-index、不加 class="clip",因此不会被 Clip 生命周期隐藏,在整个组合件存活期间持续可见。它的每一帧状态都来自时间线的 tl.time(),保证确定性渲染。
内容层 <section>:每个场景是带 class="clip transparent" 的定时层。data-start/data-duration 声明可见时间窗,data-track-index 放在同一轨道(示例均为 1,时间上首尾相接不重叠,符合 tracks-and-clips.md 的同轨不可重叠规则)。transparent 意味着背景完全透明,让下层 #bg 透出来。
时间线驱动背景:两个 tl.to(...) 以绝对时间位置参数(0 与 6)把 #bg 的背景色从深蓝 #0a1530 平滑过渡到暗紫 #1a0a30。因为背景被时间线驱动而非 Clip 机制驱动,整段 20 秒内它是一块连续运动表面——这正是「背景属于运动语言」的体现。而场景内容层各自做局部动画(如 h1 的 y/opacity 入场),透明叠加在背景之上。
让它可运行的 CSS
参考 minimal-composition.md 的布局契约,组合件还需要这样一段骨架样式:
body {
margin: 0;
background: #000;
}
#root {
position: relative;
width: 1920px; /* 显式像素尺寸:根未定尺寸是静默布局 bug */
height: 1080px;
overflow: hidden;
}
.full-bleed {
position: absolute;
inset: 0;
}
.clip.transparent {
position: absolute;
inset: 0;
background: transparent; /* 内容层透明,让共享背景透出 */
}
注意 SKILL.md 强调:独立根元素需要显式定尺寸盒子(px),且祖先链直到某个 height:100% 元素都必须解析出高度,否则 flex/100% 子元素会塌缩成 ~0 并堆到左上角——lint/validate/inspect 都抓不到这类问题。
四、五条规则详解
原文档把模式提炼为五条硬规则,每一条都与 HyperFrames 运行时契约深度绑定:
规则 1:背景不是 Clip
共享背景不得携带 data-start/data-duration/data-track-index,不加 class="clip"。它服务于整个组合件。这与 class="clip" 的运行时语义互为镜像(见 data-attributes.md):带 class="clip" 的可见定时元素才受 data-start/data-duration 显隐控制,缺了它反而全程可见。把背景做成 Clip 等于给它套上显隐窗口,视觉连续性就会在换场时断裂。
规则 2:内容场景必须透明
内容层背景必须透明(background: transparent 或干脆不设背景),让共享 #bg 中的一切透出。这条与规则 1 配合,才产生「背景连续、场景轻量」的效果。
规则 3:全局状态只从共享层驱动一次
色相偏移、暗角、颗粒、胶片滤镜这类全局视觉状态,只在共享层动画一次,绝不要按场景复制。示例中若想加全局暗角,就应把它做成覆盖全画布的 #bg 之上的兄弟元素(或 #bg 内部的伪元素/子层),用同一时间线驱动其 opacity——改动一处即可作用于所有场景。
规则 4:不要在 .clip 元素上动画可见性
HyperFrames 运行时已经根据 data-start/data-duration 负责 Clip 的显示/隐藏。若你再在 .clip 元素上动画 display/visibility,会与框架自身的显隐机制互相竞争(race),时序结果不可预测。这与 determinism-rules.md 的「可动画属性白名单」完全一致:用 opacity/变换来表达过渡,用定时 Clip 可见性来表达显隐。需要做显隐式动画时,去动画 Clip 内部的子包装元素(child wrapper),而不是 Clip 本身。
规则 5:用 snapshot 验证「有意的溢出」
在加 data-layout-allow-overflow 去消除 inspect 警告之前,先运行 npx hyperframes snapshot 并人工确认那个溢出正是你想要的。原因在 lint-validate-inspect.md 与 data-attributes.md 中写得很清楚:
inspect用getBoundingClientRect在采样时间戳上量测,而非量渲染像素——overflow: hidden会裁掉视觉,却不会消除inspect的溢出发现;data-layout-allow-overflow才是逃生舱,CSSoverflow不是。- 该属性会沿子树继承(感知探针会向上走祖先链),同时会压制
text-clipping、content-cramped-container、foreground-over-panel等渲染级感知检查。因此优先最小范围逃生:把data-layout-allow-overflow放在最小的装饰包装元素上,而不是整块常驻面板;primary-offscreen与foreground-over-panel两个画布/边缘检查即使在allow-overflow下仍会运行,它掩盖不了被画框切掉的 wordmark 或溢出到面板边缘的文字。 - 与动画耦合的实践是:进场/退场动画的中间态溢出属于「有意溢出」,应在构建时在场景主元素/共享层标注好,而不是等
inspect报出后再逐个打补丁。
五、何时不要用这个模式
文档对边界定义得很克制:如果场景在视觉上真的互不相干——不同色彩世界之间的硬切、毫无连续性——那么堆叠不透明场景(stacked-opaque)完全够用,不必引入共享层。共享背景模式只适用于背景本身就是运动语言的一部分的组合件,而不只是可有可无的衬底。
实践中可以这样判断:
- 背景全程是单一静态底色,只是换场时整体变一下 → 不需要共享层,直接在内容场景里各自设底色即可;
- 背景承载色相漂移、暗角渐变、颗粒质感、光斑流动,且跨越多个 Clip 连续演进 → 使用共享层;
- 场景间存在「同一元素跨场延续」(一个持续长大的标题词、一张跨越切点的话术列表、单一 canvas 的内部相位变化)→ 更进一步可参考 composition-patterns.md 的多场景合并子组合件(内部相位 div)与「单文件 vs 子组合件」取舍,让共享状态留在同一个子组合件内部。
六、放入工程:模块化项目里的背景与媒体分层
当项目是多场景模块化结构(index.html + compositions/<scene>.html)时,全屏运动模式依然成立,但要注意层归属(详见 composition-patterns.md 的 Modular Orchestrator Pattern):
- 共享背景应属于宿主根(host root),而不是子组合件。
index.html保持瘦身:根元素直接放置#bg全出血层与若干子组合件槽位<div data-composition-src=...>,槽位视觉场景共用同一个低轨道(如1),顺序相接;需要交叉淡化时把其中一个放到更高轨道并重叠淡入时长。 - 媒体必须直属于宿主根。
<video>/<audio>若被包进子组合件<template>或任意包装<div>,永远不会被 seek/解码,渲染为黑帧——这是lint/validate/inspect都不会抓到的盲点。跨场景连续 BGM 以根级<audio>存在,轨道索引应明显高于视觉轨道(如10)。 - 根时间线保持近空。 所有场景动画在子组合件内部;共享背景的色相过渡这类「全局状态」如需由根时间线驱动,请用全局时间坐标(场景槽
data-start+ 场景内偏移),并遵守「每个组合件只注册一条 paused 时间线」的契约。
从 OpenMontage 工程层面看,这一整条 HTML 渲染路径由 hyperframes_compose.py 拥有:当 edit_decisions.render_runtime == "hyperframes" 时,video_compose 会调用它完成工作区物化、hyperframes lint/check 与 hyperframes render,属于 capability="video_post"、确定性(DETERMINISTIC)的 CORE 级工具;其安装说明强调 HyperFrames CLI 通过 npx hyperframes 获取(npm 包名 hyperframes,内部名 @hyperframes/cli 不在公共 npm 上,会 404),并可用 npx hyperframes doctor 校验环境。
七、验证闭环:动手确认模式写对了
文档规定的验证顺序是静态 → 运行时 → 布局 → 抽帧(命令详见 lint-validate-inspect.md):
npx hyperframes lint # 静态契约:同轨重叠、未注册时间线等
npx hyperframes validate # headless Chrome 运行时:console 错误、WCAG AA 对比度
npx hyperframes inspect # 布局巡检:文字溢出容器/画布、被裁剪等
npx hyperframes snapshot --frames 10 # 抽 10 帧 PNG,肉眼核对共享背景过渡与溢出
npx hyperframes render --quality standard # 用户批准后才渲染 MP4
针对全屏运动模式,重点核对:
#bg不应触发任何 Clip 相关告警(它没有data-start/data-duration);inspect若报告溢出,先用snapshot判定是否为入场/退场动画的有意中间态,再决定最小范围加data-layout-allow-overflow;snapshot --at <midpoints>检查跨场中间帧:背景色过渡是否顺滑连续,内容层是否透明透出背景(而不是带出不透明底色块);- 对动画驱动的组合件,可在组合件旁放
*.motion.json边车文件,让inspect自动断言入场时序、帧内驻留与动画活性,把「看 MP4」变成可自动化的检查(详见 lint-validate-inspect.md 的 Motion verification 一节)。
八、小结
Full-Screen Motion Pattern 是 HyperFrames 确定性渲染模型下处理全帧运动的标准答案:一块非 Clip 的全出血共享背景层承载所有全局视觉状态,由 paused 的可 seek 时间线连续驱动;场景退化为透明的定时内容层,只管自己的局部动画。它把「逐帧 seek 仍要像素级可复现」的硬约束,转化为「背景即运动、场景即透明的分层结构」,同时规避了根元素背景被合成丢弃、display/visibility 与框架显隐竞态、以及滥用 data-layout-allow-overflow 压掉感知检查这三类常见陷阱。
相关延伸阅读:模式总览见 hyperframes-core/SKILL.md 的 References 索引;时间线/轨道规则见 tracks-and-clips.md;单文件与模块化架构取舍见 composition-patterns.md;确定性契约与可动画属性白名单见 determinism-rules.md;CLI 正确性流水线见 lint-validate-inspect.md。在 OpenMontage 中,HyperFrames 技能族的分工(core / animation / creative / media / cli / registry)见 .agents/skills/hyperframes/ 与 hyperframes.md 的能力映射。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00