跳到主要内容
BLKBLKTECH

指南 / ai

HyperFrames 模板化进阶:用片头—内容—片尾架构批量生产视频

把精修片头、动态内容和固定片尾拆成可复用 Sub-composition,通过变量、场景清单、根级音频与自动验证建立稳定的 HyperFrames 批量视频生产线。

作者 BLKTECH 编辑部更新 2026年7月26日28 分钟难度 进阶低成本HyperFramesHTMLCSSGSAPJSONFFmpegAI Agent

HyperFrames 的片头—内容—片尾模板架构,本质是一个薄主 Composition 加多个可独立 Seek 的 Sub-composition:主入口只负责编排时间窗口、变量和根级音频,精修片头与片尾作为版本化模板复用,AI Agent 只生成或组装中间内容场景。

先说结论:思路正确,但要从“复制模板”升级为“Composition 编排”

把一条视频拆成:

精修片头
+ 动态内容
+ 精修片尾

确实是程序化视频生产中非常有效的架构。

它可以把稳定的品牌资产和每期变化的内容分开,让 AI Agent 不必每次重新设计 Logo 动画、频道标识、CTA 和音频收尾,只需要集中处理本期真正变化的部分。

这套模式可以形象地称为:

  • 夹心模板;
  • Sandwich Architecture;
  • 沙漏型生产线;
  • Brand Shell + Dynamic Body。

不过需要先澄清:

“Sandwich Architecture”适合作为团队内部的架构名称,但 HyperFrames 技术文档中的对应概念是 Modular Orchestrator、Sub-composition、Variables 和 Tracks。

真正可靠的实现,不是每次把三段 HTML 复制到同一个文件,而是让:

index.html
只负责时间编排、变量注入、音频和全局验证

compositions/intro.html
负责精修片头

compositions/body-*.html
负责每期动态内容

compositions/outro.html
负责精修片尾

每个场景拥有自己的 DOM、CSS 和 paused Timeline,由 HyperFrames 根据 data-start 独立 Seek。

为什么夹心模板适合 AI 视频生产

1. 它把品牌质量下限固定下来

AI 每次从空白画布生成视频,最容易漂移的是:

  • Logo 出现方式;
  • 字体大小和层级;
  • 品牌颜色;
  • 标题落版;
  • 片尾 CTA;
  • 转场速度;
  • BGM 收尾方式。

这些内容如果每集都重新生成,技术上可能正确,品牌感却会不断变化。

把片头和片尾沉淀为经过人工确认的 Sub-composition 后,即使中间内容由不同 Agent、不同 Prompt 或不同 Registry Block 生成,视频仍然拥有稳定的首尾识别。

2. 它缩小了 Agent 的创作边界

没有模板时,Agent 需要同时决定:

怎么开场
怎么介绍标题
正文分几幕
每幕怎么动
怎么显示 Logo
怎么做 CTA
音乐什么时候结束

模板化后,任务可以收窄为:

不要修改 intro 和 outro。
根据 STORYBOARD 生成 body scenes。
输出本期变量和场景时间表。

这不仅减少生成内容,也减少了决策分支、风格漂移和返工概率。

实际节省多少时间,取决于中间内容的复杂度、模板成熟度和自动验证程度,因此不宜承诺固定的“提升 80%”或“30 秒出片”。更可靠的衡量方式是:

  • 每集需要新增多少场景代码;
  • 人工需要检查多少关键帧;
  • 品牌模板被意外修改的次数;
  • 从脚本确认到草稿预览所需时间;
  • 同一模板可以稳定产出多少个版本。

3. 它让资产真正可复用

可复用的不只是片头和片尾文件,还包括:

  • 标题变量 Schema;
  • 品牌设计 Token;
  • Logo 和字体资产;
  • BGM Ducking/Fade 规则;
  • 固定 Snapshot 检查点;
  • 横屏、竖屏和方屏的模板版本;
  • 批量渲染所需的 Episode Manifest。

当这些资产被版本化后,视频生产会从“每次写一条视频”变成“给成熟系统输入新一期数据”。

正确的三层架构

一条标准 36 秒教程,可以拆成:

0s ───── 3s ─────────────── 31s ───── 36s
│ Intro  │      Body Stack       │ Outro │
│ 3 秒    │       28 秒            │ 5 秒   │

三个层级的职责应该明确分离。

Intro:建立问题和品牌识别

片头负责:

  • 在最短时间内说明本期主题;
  • 展示频道或品牌识别;
  • 建立第一拍声音和动作;
  • 把观众带入正文。

它可以暴露:

title
kicker
accent
logo

但不要暴露几十个布局变量。变量越多,模板越容易被每一期改成不同风格。

Body Stack:承载真正变化的内容

中间内容负责:

  • 教程步骤;
  • 产品演示;
  • 代码或终端;
  • 数据对比;
  • 流程图;
  • 本期特有图片和视频。

这里可以由 Agent:

  • 查询 Registry;
  • 安装合适的 Block;
  • 生成新的场景 Sub-composition;
  • 根据 JSON 或变量替换内容;
  • 计算场景开始时间和时长。

Outro:完成收束和下一步行动

片尾负责:

  • 总结结果;
  • 展示 CTA;
  • 提示下一集;
  • 显示频道标识;
  • 配合根级 BGM 完成淡出。

它可以暴露:

cta
next_title
handle
accent

片尾不应该重新讲一遍正文,也不应该突然切换成完全不同的视觉语言。

原始组装示例里有哪些技术问题

很多“片头 + Body + 片尾”示例看起来合理,却没有满足 HyperFrames 的完整 Composition 合约。

下面这种写法存在问题:

<div
  data-composition-id="01-intro"
  data-composition-src="./templates/intro.html"
  data-duration="4"
>
  <script type="application/json">
    { "VIDEO_TITLE": "本期标题" }
  </script>
</div>

主要问题包括:

  1. 缺少主 Composition 根节点的 data-widthdata-height 和总 data-duration
  2. 子 Composition Host 缺少 data-start
  3. 缺少 data-track-index
  4. 缺少 data-widthdata-height
  5. Host 的 data-composition-id 必须与子文件内部 ID、Timeline Key 一致;
  6. 变量不应通过随意嵌套的 JSON <script> 传递;
  7. HyperFrames 的 Per-instance 变量应该使用 data-variable-values
  8. Body 不能只是一个没有时间窗口的普通空 <div>
  9. 子 Composition 文件必须把运行所需的 Style、DOM 和 Script 放进 <template>
  10. 根级 BGM 不能由 Outro 的子 Timeline 跨边界控制。

因此,夹心模板的关键不是视觉概念,而是正确实现 Host 与 Sub-composition 之间的挂载合约。

推荐的项目目录

可以把一次性内容、品牌模板和生成数据分开:

video-project/
├── index.html
├── BRIEF.md
├── STORYBOARD.md
├── frame.md
├── episode.json
├── compositions/
│   ├── brand-intro.html
│   ├── body-terminal.html
│   ├── body-compare.html
│   ├── body-summary.html
│   └── brand-outro.html
├── assets/
│   ├── brand/
│   │   ├── logo.svg
│   │   └── mark.svg
│   ├── audio/
│   │   ├── bgm.wav
│   │   ├── intro-whoosh.wav
│   │   └── outro-confirm.wav
│   └── fonts/
├── data/
│   └── theme.tokens.json
└── renders/

如果片头片尾会被多个项目共用,可以在团队模板仓库中维护源文件,再安装或复制一个明确版本到当前项目:

templates/brand-shell/v1/
templates/brand-shell/v2/

不要让每次渲染都依赖工作区外部不断变化的远程 HTML。最终进入项目的模板、字体、Logo 和音频应该是本地、可追踪、可复现的资产。

第一步:建立一个薄的主 Composition

index.html 的任务应该尽量少:

声明总画幅和总时长
编排每个场景的时间窗口
向场景传入变量
挂载连续 BGM 和全局 SFX
注册根级 Timeline

一个完整示例:

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <script src="./vendor/gsap.min.js"></script>
    <style>
      body {
        margin: 0;
        background: #07090f;
      }

      #episode-root {
        position: relative;
        width: 1920px;
        height: 1080px;
        overflow: hidden;
        background: #07090f;
      }

      #episode-root > div[data-composition-src] {
        position: absolute;
        inset: 0;
      }
    </style>
  </head>
  <body>
    <div
      id="episode-root"
      data-composition-id="episode-007"
      data-width="1920"
      data-height="1080"
      data-duration="36"
    >
      <div
        id="el-intro"
        data-composition-id="brand-intro"
        data-composition-src="compositions/brand-intro.html"
        data-variable-values='{
          "title":"3 分钟搞定 Claude Code 自动化",
          "kicker":"BLKTECH / AI WORKFLOW",
          "accent":"#f7e34d"
        }'
        data-start="0"
        data-duration="3"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>

      <div
        id="el-terminal"
        data-composition-id="body-terminal"
        data-composition-src="compositions/body-terminal.html"
        data-variable-values='{
          "step":"STEP 01",
          "command":"npx hyperframes catalog --json",
          "accent":"#f7e34d"
        }'
        data-start="3"
        data-duration="8"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>

      <div
        id="el-compare"
        data-composition-id="body-compare"
        data-composition-src="compositions/body-compare.html"
        data-variable-values='{
          "leftValue":"120 min",
          "rightValue":"3 min",
          "accent":"#f7e34d"
        }'
        data-start="11"
        data-duration="10"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>

      <div
        id="el-summary"
        data-composition-id="body-summary"
        data-composition-src="compositions/body-summary.html"
        data-start="21"
        data-duration="10"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>

      <div
        id="el-outro"
        data-composition-id="brand-outro"
        data-composition-src="compositions/brand-outro.html"
        data-variable-values='{
          "cta":"关注频道,获取完整工作流",
          "nextTitle":"下一集:批量渲染多个版本",
          "accent":"#f7e34d"
        }'
        data-start="31"
        data-duration="5"
        data-track-index="1"
        data-width="1920"
        data-height="1080"
      ></div>

      <audio
        id="episode-bgm"
        src="assets/audio/bgm.wav"
        data-start="0"
        data-duration="36"
        data-track-index="10"
        data-volume="0.18"
      ></audio>

      <audio
        id="intro-whoosh"
        src="assets/audio/intro-whoosh.wav"
        data-start="0.42"
        data-duration="0.8"
        data-track-index="20"
        data-volume="0.75"
      ></audio>

      <audio
        id="outro-confirm"
        src="assets/audio/outro-confirm.wav"
        data-start="32.1"
        data-duration="0.7"
        data-track-index="20"
        data-volume="0.7"
      ></audio>
    </div>

    <script>
      window.__timelines = window.__timelines || {};

      const rootTimeline = gsap.timeline({ paused: true });

      // BGM 位于 Host Root,所以必须由根 Timeline 在全局时间控制。
      rootTimeline.to("#episode-bgm", {
        volume: 0,
        duration: 2.4,
        ease: "power2.inOut"
      }, 33.6);

      window.__timelines["episode-007"] = rootTimeline;
    </script>
  </body>
</html>

这个主文件没有处理片头标题如何进入,也没有处理片尾卡片如何发光。那些动作属于各自 Sub-composition 的内部 Timeline。

主文件只知道:

哪一幕
从什么时候开始
显示多久
在哪一层
使用哪些变量

这就是 Modular Orchestrator 的价值。

第二步:编写真正可复用的 Intro Sub-composition

子 Composition 不是普通独立网页。HyperFrames 挂载时只克隆 <template> 内的内容,因此运行所需的:

  • <style>
  • DOM;
  • <script>
  • 自定义元素注册;

都必须位于 <template> 内。

<!doctype html>
<html
  lang="zh-CN"
  data-composition-variables='[
    {
      "id":"title",
      "type":"string",
      "label":"Video title",
      "default":"本期视频标题",
      "maxLength":28
    },
    {
      "id":"kicker",
      "type":"string",
      "label":"Series kicker",
      "default":"BLKTECH / AI WORKFLOW"
    },
    {
      "id":"accent",
      "type":"color",
      "label":"Accent color",
      "default":"#f7e34d"
    }
  ]'
>
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <template id="brand-intro-template">
      <style>
        #intro-root {
          position: absolute;
          inset: 0;
          overflow: hidden;
          color: #fff;
          background:
            radial-gradient(circle at 70% 30%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 34%),
            #07090f;
        }

        #intro-kicker {
          position: absolute;
          top: 110px;
          left: 120px;
          color: var(--accent);
          font: 700 24px/1.2 monospace;
          letter-spacing: .12em;
        }

        #intro-title {
          position: absolute;
          left: 120px;
          bottom: 150px;
          width: 1500px;
          margin: 0;
          font: 800 104px/1.02 sans-serif;
          letter-spacing: -.045em;
        }

        #intro-accent-line {
          position: absolute;
          left: 120px;
          bottom: 110px;
          width: 520px;
          height: 10px;
          background: var(--accent);
          transform-origin: left center;
        }
      </style>

      <div
        id="intro-root"
        data-composition-id="brand-intro"
        data-width="1920"
        data-height="1080"
        data-duration="3"
      >
        <div id="intro-kicker" data-var-text="kicker">BLKTECH / AI WORKFLOW</div>
        <h1 id="intro-title" data-var-text="title">本期视频标题</h1>
        <div id="intro-accent-line"></div>
      </div>

      <script>
        window.__timelines = window.__timelines || {};

        const introTimeline = gsap.timeline({ paused: true });

        introTimeline.fromTo("#intro-kicker", {
          opacity: 0,
          y: 20
        }, {
          opacity: 1,
          y: 0,
          duration: 0.35,
          ease: "power3.out"
        }, 0.15);

        introTimeline.fromTo("#intro-title", {
          opacity: 0,
          y: 70,
          scale: 0.94
        }, {
          opacity: 1,
          y: 0,
          scale: 1,
          duration: 0.62,
          ease: "power4.out"
        }, 0.35);

        introTimeline.fromTo("#intro-accent-line", {
          scaleX: 0
        }, {
          scaleX: 1,
          duration: 0.45,
          ease: "power3.inOut"
        }, 0.72);

        window.__timelines["brand-intro"] = introTimeline;
      </script>
    </template>
  </body>
</html>

这里必须同时满足三个 ID 一致:

Host data-composition-id="brand-intro"
Sub-composition root data-composition-id="brand-intro"
window.__timelines["brand-intro"]

如果 Host 为了“看起来更整齐”把 ID 改成 01-intro,内部仍然叫 brand-intro,运行时就可能找不到正确 Timeline。

第三步:为 Outro 定义有限变量

片尾模板可以使用类似结构:

<html
  data-composition-variables='[
    {
      "id":"cta",
      "type":"string",
      "label":"CTA",
      "default":"关注频道,获取下一期教程",
      "maxLength":32
    },
    {
      "id":"nextTitle",
      "type":"string",
      "label":"Next episode",
      "default":"下一集:完整自动化工作流",
      "maxLength":40
    },
    {
      "id":"accent",
      "type":"color",
      "label":"Accent",
      "default":"#f7e34d"
    }
  ]'
>

变量设计应该遵循:

内容可以变化
布局不能随意变化
品牌动作不能随意变化

不要把以下内容都暴露给每一期:

cardX
cardY
logoScale
shimmerSpeed
borderRadius
fontSize
rotation

否则 Agent 虽然没有修改模板源码,却仍然可以通过变量把模板改得面目全非。

模板应该提供少量语义变量,而不是把所有 CSS 属性都变成控制面板。

片头应该多长

固定模板不等于固定五秒 Logo 动画。

对于短教程、社交视频和产品演示,更实用的片头通常是:

0.0~0.4 秒:问题或视觉冲击
0.4~1.2 秒:标题建立
1.2~2.4 秒:标题停留并准备转场
2.4~3.0 秒:进入正文

片头可以展示 Logo,但 Logo 不应该阻止本期主题尽快出现。

相比:

先看 5 秒 Logo
→ 再告诉观众视频讲什么

更好的顺序是:

Logo 与本期痛点同时出现
→ 1 秒内让观众知道值不值得继续看

长视频栏目可以拥有更完整的 Opening,但它也应该经过真实留存数据验证,而不是因为动效制作昂贵就默认必须完整播放。

根级 BGM 为什么不能交给 Outro 控制

这是夹心架构中非常重要的边界。

假设 BGM 放在 index.html

<audio id="episode-bgm" ...></audio>

而 Outro 动画位于 compositions/brand-outro.html

Outro 的 Timeline 只能驱动自己的子树,不能通过:

gsap.to("#episode-bgm", { volume: 0 });

跨越 Sub-composition 边界控制 Host Root 的音频。

正确做法是:

  • Outro 内部 Timeline 负责 CTA、Logo、Shimmer 和文字;
  • 主 Timeline 负责根级 BGM 在全局时间 33.6s 开始淡出;
  • SFX 可以放在根级并用 data-start 指定落点;
  • 如果音频只属于某一个场景,也可以放在对应 Sub-composition 内,以场景本地时间管理。

连续跨场景的 BGM 放在 Root,更容易保持播放连续性并统一处理 Ducking 与 Fade Out。

关于 Voice、BGM 和 SFX 的完整分层,可以继续阅读《HyperFrames 音频进阶:修好 TTS 旁白,重构无旁白视频节奏》

“全局 CSS Theme 继承”不能只靠一个父级变量

片头、Body 和片尾需要统一设计 Token,这个方向正确,但 Sub-composition 拥有独立、被作用域化的 CSS,不能把它简单理解成普通网页组件会自动继承全部 Host Theme。

更稳妥的方案有两种。

方案一:对每个场景传入同一组语义变量

data-variable-values='{
  "accent":"#f7e34d",
  "surface":"#07090f",
  "textColor":"#ffffff"
}'

每个 Sub-composition 声明自己需要的变量,并通过 CSS Custom Property 使用:

#scene-root {
  color: var(--textColor);
  background: var(--surface);
}

.scene-accent {
  color: var(--accent);
}

方案二:由构建阶段展开共享 Token

维护一份:

{
  "color": {
    "surface": "#07090f",
    "text": "#ffffff",
    "accent": "#f7e34d"
  },
  "radius": {
    "card": 28
  },
  "motion": {
    "enter": 0.55,
    "exit": 0.35
  }
}

让 Agent 或构建脚本在写入 Composition 时生成稳定的本地 CSS。这样可以共享品牌规范,又不会在渲染阶段依赖远程主题文件或异步请求。

推荐组合是:

稳定设计规范 → theme.tokens.json
每期可变配色 → data-variable-values

Body 不应该是一个无限自由的黑箱

如果只告诉 Agent:

中间 30 秒自由发挥。

模板只能固定首尾,正文仍然会出现风格漂移。

更成熟的 Body Stack 应该由有限场景类型组成:

场景类型 用途 常见变量
Terminal 命令演示 step、command、result
Code Focus 代码高亮 language、code、focusLine
Compare 前后对比 leftValue、rightValue、labels
Process 流程说明 title、steps、accent
Big Number 数据结论 value、unit、caption
Summary 总结收束 points、result

这相当于给 Agent 一套“视觉语法”。它可以决定本期使用什么句子和数据,但不能无限创造新的布局规则。

在使用 Registry 前,应先查询当前 Catalog:

npx hyperframes catalog --json

不要默认 terminal-blockcode-editorbento-grid 是当前 Registry 中的精确 Item Name。查询后还要区分:

  • Block:独立 Sub-composition,可以通过 data-composition-src 挂载;
  • Component:需要把片段合并进现有场景;
  • Example:用于初始化或参考完整工程。

用 Episode Manifest 描述每一期

当场景类型稳定后,不应该让 Agent 直接从自然语言跳到最终 index.html。中间增加一份 Episode Manifest,会更容易检查和自动化。

{
  "episodeId": "episode-007",
  "width": 1920,
  "height": 1080,
  "duration": 36,
  "theme": {
    "accent": "#f7e34d",
    "surface": "#07090f"
  },
  "intro": {
    "compositionId": "brand-intro",
    "src": "compositions/brand-intro.html",
    "start": 0,
    "duration": 3,
    "variables": {
      "title": "3 分钟搞定 Claude Code 自动化",
      "kicker": "BLKTECH / AI WORKFLOW"
    }
  },
  "scenes": [
    {
      "compositionId": "body-terminal",
      "src": "compositions/body-terminal.html",
      "start": 3,
      "duration": 8,
      "variables": {
        "step": "STEP 01",
        "command": "npx hyperframes catalog --json"
      }
    },
    {
      "compositionId": "body-compare",
      "src": "compositions/body-compare.html",
      "start": 11,
      "duration": 10,
      "variables": {
        "leftValue": "120 min",
        "rightValue": "3 min"
      }
    }
  ],
  "outro": {
    "compositionId": "brand-outro",
    "src": "compositions/brand-outro.html",
    "start": 31,
    "duration": 5,
    "variables": {
      "cta": "关注频道,获取完整工作流",
      "nextTitle": "下一集:批量渲染多个版本"
    }
  }
}

这份 JSON 的职责是描述“组装结果”,不是让 Composition 在渲染时通过网络异步获取数据。

推荐在构建阶段完成:

BRIEF + SCRIPT
→ Agent 生成 episode.json
→ 校验 Schema 和时间窗口
→ 生成或更新 index.html
→ HyperFrames 检查与渲染

如果需要复杂数组、循环或动态派生内容,也应该在初始化阶段同步读取已经内联或本地冻结的数据,不要在 Timeline 建立后再等待异步请求。

Variables 适合什么,不适合什么

HyperFrames Variables 很适合处理:

标题
数字
颜色
布尔开关
有限枚举
图片或视频地址
CTA
语言版本

它不意味着任何复杂 JSON 都会自动变成视频布局。

例如,一个含有 20 个步骤和多层嵌套图表的对象,仍然需要:

  • 明确的 JSON Schema;
  • 对应的场景 Renderer;
  • 文本溢出规则;
  • 最大条目数;
  • 数据为空时的降级方案;
  • 画面时长计算逻辑。

所以“中间完全由 JSON 驱动”的完整表达应该是:

Body 使用经过设计的 Schema 和 Renderer 接收结构化数据,而不是把任意 JSON 直接丢给 HyperFrames。

如何保护片头片尾不被 Agent 意外改坏

“固定模板”不应该只靠 Prompt 中一句“请勿修改”。可以增加工程边界。

1. 明确写入范围

给 Agent 的任务中注明:

只允许修改:
- episode.json
- compositions/body-*.html
- STORYBOARD.md

禁止修改:
- compositions/brand-intro.html
- compositions/brand-outro.html
- assets/brand/
- data/theme.tokens.json

2. 对模板做版本控制

brand-intro@1
brand-outro@1

当品牌需要升级时,创建 V2 并重新通过视觉审核,不要在某一期正文任务中顺手修改 V1。

3. 保存黄金关键帧

为片头和片尾固定几个检查点:

Intro:0.2s / 0.8s / 1.6s / 2.8s
Outro:31.1s / 32.2s / 34.0s / 35.8s

模板升级后,将新 Snapshot 与已确认的 Golden Frames 对比。

4. 给变量设置限制

使用:

maxLength
min / max
step
选择型 enum

避免 60 字标题被注入只为 16 字设计的片头。

5. CI 中检查未声明变量

批量渲染或持续集成时使用严格变量检查,及时发现拼写错误、类型不匹配和多余字段。

时间窗口比 Timeline 长度更重要

Host 上的 data-duration 定义 Sub-composition 的可见窗口。

如果内部动画只有 2 秒,而 Host Window 是 3 秒:

前 2 秒播放动画
最后 1 秒保持最终画面

这通常正适合片头标题停留。

如果内部动画是 5 秒,而 Host Window 只有 3 秒:

3 秒时场景被截断
后面的动画不会显示

因此模板应该定义:

推荐窗口时长
最短安全时长
关键动作完成时间
允许停留时间

例如:

brand-intro
推荐时长:3.0s
最短时长:2.4s
标题完成:1.1s
安全离场:2.35s

不要随意在 Host 中把一个 5 秒片尾压缩成 2 秒,然后期待 HyperFrames 自动重新缩放内部 Timeline。

同轨顺序与跨轨转场

顺序片头、Body 和片尾通常可以放在同一个视觉 Track:

track-index = 1

并让时间窗口首尾相接:

Intro  0–3
Body   3–31
Outro 31–36

如果需要 Crossfade,不要让两个重叠场景放在同一 Track。可以使用:

旧场景:track 1
新场景:track 2
重叠 0.3~0.6 秒

并由各自 Timeline 对内部包装层做 opacity 变化。

更高的 Track 会显示在更前面。Track 是时间与层级结构,不应该被当成随意增加的 CSS z-index 替代品。

片尾如何做得精致但不拖沓

片尾可以有更完整的品牌动作,但它仍然需要服务下一步行动。

一个五秒片尾可以拆成:

0.0~0.5 秒:正文结果延续到 CTA
0.5~1.4 秒:CTA 卡片建立
1.4~2.4 秒:Shimmer 或边框流光经过
2.4~4.0 秒:下一集标题停留
4.0~5.0 秒:Logo、画面和 BGM 一起收束

视觉上可以使用:

  • Shimmer Sweep;
  • 轻微发光边框;
  • Logo Lockup;
  • 下一集标题;
  • 订阅或关注提示;
  • 小幅度尺度呼吸。

但 CTA 卡片不应该一直高速闪烁,也不要让装饰动画压过可点击或可记忆的信息。

如果片尾需要 Registry Component,先查询实际名称和类型,再决定是安装为 Component 还是使用自己的品牌实现。

多画幅不能只复用同一个模板

16:9 片头直接缩放成 9:16,通常会出现:

  • 标题过宽;
  • Logo 位置失衡;
  • 手机安全区被遮挡;
  • CTA 卡片太小;
  • 横向动作在竖屏中缺少空间。

推荐维护:

brand-intro-landscape.html
brand-intro-portrait.html
brand-outro-landscape.html
brand-outro-portrait.html

它们可以共享:

  • Logo;
  • 品牌颜色;
  • 字体;
  • 动作节奏;
  • Variable Schema;
  • Episode 数据。

但构图应该针对画幅重新设计,而不是依赖普通网页响应式布局自动解决。

一条完整的 Agent 生产流程

模板系统成熟后,可以把每期流程固定为:

1. 读取 BRIEF、SCRIPT、frame.md
2. 锁定画幅、语言、视频模式和目标时长
3. 不修改品牌 Intro/Outro
4. 把正文拆成有限场景类型
5. 查询 Catalog,确认可复用 Block/Component
6. 生成 episode.json
7. 校验场景时间是否连续、是否越界
8. 生成或更新 body Sub-compositions
9. 生成薄 index.html 编排层
10. 挂载根级 Voice/BGM/SFX
11. lint
12. check
13. 检查 Intro/Outro 黄金关键帧
14. 检查每个 Body 场景的进入、高潮、停留和离场
15. preview
16. 人工确认后 render

如果是有旁白视频,应该先锁定真实语音时长,再计算 Body 与 Outro 的开始时间。不要为了保持片尾固定在第 31 秒,强行截断前面的旁白。

一份可直接交给 Agent 的模板化指令

请使用 HyperFrames 的 Modular Orchestrator + Sub-composition 架构制作本期视频。

架构:
- Intro:使用 compositions/brand-intro.html;
- Body:由你生成或组装 compositions/body-*.html;
- Outro:使用 compositions/brand-outro.html;
- index.html 只负责时间窗口、变量、Tracks、根级音频和根 Timeline。

写入边界:
- 不允许修改 brand-intro.html、brand-outro.html 和 assets/brand;
- 可以修改 episode.json、STORYBOARD.md、index.html 和 body scenes;
- 如果现有模板无法容纳内容,先报告变量或布局限制,不要直接改品牌模板。

Sub-composition 规则:
- Host data-composition-id 必须与子文件 root ID 和 Timeline Key 一致;
- Host 必须声明 data-start、data-duration、data-track-index、data-width、data-height;
- 子文件的 style、DOM 和 script 必须全部位于 template 内;
- 使用 data-variable-values 传入每期变量;
- 使用 fromTo 声明明确起止状态;
- 不要把子 Timeline 手动加入主 GSAP Timeline;
- 不允许子 Timeline 跨边界控制 Host 元素。

品牌规则:
- 所有场景使用 theme.tokens.json 中的颜色、字体、圆角和动作节奏;
- 每个 Body Scene 只解决一个视觉问题;
- Intro 在 1 秒内显示本期主题;
- Outro 显示一个主 CTA 和一个下期标题;
- 根级 BGM 的 Fade Out 由主 Timeline 在全局时间控制。

数据规则:
- 先生成 episode.json;
- 场景必须使用已定义的 Schema 和 Renderer;
- 不要假设任意 JSON 能自动变成视频;
- Registry 名称必须通过 npx hyperframes catalog --json 查询;
- 变量需要默认值、长度限制和合理降级。

验证:
- 检查时间窗口是否连续且不越过总时长;
- 检查同 Track 是否发生意外重叠;
- 对 Intro/Outro 执行固定 Snapshot;
- 对每个 Body 场景检查进入、高潮、停留、离场;
- 依次执行 lint、check、snapshot、preview;
- 最终预览确认前不要渲染高质量 MP4。

常见错误诊断表

问题 原因 修复方式
片头挂载后没有样式 <style> 放在子文件 <head> 把 Style、DOM、Script 全部移入 <template>
片头静止不动 Host ID、内部 ID、Timeline Key 不一致 三处统一使用同一个 Composition ID
变量没有生效 使用了自定义 JSON <script> 改用 data-variable-values 和变量声明
Body 结束后画面变空 Host Window 没有覆盖后续时间 修正 data-startdata-duration
片尾动画被截断 Host Duration 小于关键动作时间 恢复推荐时长或重做模板时间轴
BGM 没有淡出 Outro Timeline 试图操作根级 Audio 在 Root Timeline 按全局时间动画 volume
每期颜色仍然漂移 只在 Prompt 中描述品牌色 使用 Token 文件和有限颜色变量
Agent 改坏片头 没有写入边界和版本控制 限制写入范围,保存黄金关键帧
Registry Block 无法安装 使用了想象中的名称 先执行 catalog --json 查询
中间场景仍然杂乱 Body 完全自由生成 建立有限场景类型和 Schema
同时出现两幕但一幕消失 重叠场景放在同一 Track Crossfade 时使用不同 Track
Preview 与 Render 不一致 异步获取 JSON 或依赖真实时间 数据本地化,Timeline 同步建立并可 Seek

什么时候不适合使用夹心模板

这套架构并不是所有视频的答案。

以下场景可能更适合单一 Composition:

  • 整条视频是一个连续 3D 镜头;
  • 同一个 Canvas 或 WebGL 状态贯穿全片;
  • 一个 SVG 在所有阶段持续 Morph;
  • 视频只有一个连续场景,没有硬切;
  • 总工程很小,并且不会复用任何场景。

如果多个阶段共享同一个持续状态,把它们强行切成 Intro、Body、Outro 三个文件,反而会增加跨边界协调成本。

夹心模板最适合:

具有明确场景切换
具有稳定品牌首尾
中间内容重复出现若干场景类型
需要批量生成多个版本
需要让 Agent 在有限边界内工作

总结

片头—内容—片尾架构真正带来的价值,不是省掉两次复制粘贴,而是建立清晰的责任边界:

Brand Templates
负责稳定品牌识别和视觉质量

Body Scenes
负责表达每期真正变化的内容

Episode Manifest
负责描述数据、顺序和时长

Root Composition
负责编排、音频和全局时间

Validation
负责防止模板和场景在批量生产中逐渐失控

在 HyperFrames 中,最可靠的落地方式是:

薄 index.html
+ 可复用 Sub-composition
+ data-variable-values
+ 有限 Body Schema
+ 根级音频
+ 可 Seek Timeline
+ 固定 Snapshot

当片头和片尾被版本化、正文被场景化、每期内容被 Manifest 化之后,AI Agent 才真正从“每次重新设计一条视频”,升级为“在稳定的视频系统中组装新一期内容”。

系列导航

NEXT ACTION / 下一步

继续搭建 JSON 驱动的视频流水线

把读到的方法变成一个小行动,完成后再回来迭代。

继续

RELATED / 相关推荐

接着读这些

按同一栏目、标签与技术栈为你挑选。