Communitygithub.com

Parcha-ai/build

在 HyperFrames HTML 中创建视频合成、动画、标题卡片、叠加层、字幕、配音、音频响应式视觉效果和场景转场。当需要构建任何基于 HTML 的视频内容、添加与音频同步的字幕、生成文本转语音旁白、创建音频响应式动画(节拍同步、发光、脉冲)或添加场景转场时使用。

build 是什么?

此技能使 AI 代理能够使用 HyperFrames 框架构建专业的 HTML 视频内容。它采用严格的“先布局后动画”方法:首先使用静态 CSS 定位元素在其最可见的帧中,然后使用 GSAP 时间线动画进入和退出,从而确保精确布局并避免意外重叠。该技能支持完整的视频制作工作流程,包括视频和音频元素、合成嵌套(通过 `data-composition-id` 和 `data-composition-src`)、音频响应式动画(通过计算节拍同步的重复次数)和 WCAG 对比度检查。它强制执行严格的规则:不允许使用 `Math.random()` 或时间相关的逻辑(无随机性),时间线必须同步构建并在 `window.__timelines` 中注册,并且需要场景转场——除了最后一个场景外,禁止退出动画。该技能涵盖了常见陷阱,例如将视频静音并拆分为独立的 `<audio>` 元素、避免在内容文本中使用 `<br>` 以及使用 `data-track-index` 代替已弃用的 `data-layer`。它与用于 linting、预览和渲染的 `hyperframes-cli` 技能无缝协作,使开发者能够快速迭代并将 HTML 合成渲染为视频。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Parcha-ai/build/tree/HEAD/.agents/skills/hyperframes

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

预览

来自技能 README

文档

HyperFrames

HTML 是视频的单一事实来源。合成是一个带有用于计时的 data-* 属性、用于动画的 GSAP 时间线和用于外观的 CSS 的 HTML 文件。该框架处理剪辑可见性、媒体播放和时间线同步。

方法

在编写 HTML 之前,请从高层次思考:

  1. 什么 — 观众应该体验什么?确定叙事弧线、关键时刻和情感节奏。
  2. 结构 — 有多少个合成,哪些是子合成与内联合成,哪些轨道承载什么(视频、音频、叠加层、字幕)。
  3. 计时 — 哪些剪辑驱动持续时间,转场发生在哪里,节奏如何。
  4. 布局 — 首先构建最终状态。请参见下面的“先布局后动画”。
  5. 动画 — 然后使用以下规则添加运动。

对于小改动(修复颜色、调整时间、添加一个元素),直接跳到规则。

当没有提供 visual-style.md 或动画方向时,请遵循 house-style.md 获取运动默认值、大小和调色板。

先布局后动画

将每个元素放置在其最可见的时刻应处的位置——即它完全进入、正确放置且尚未退出的帧。首先将其编写为静态 HTML+CSS。不要使用 GSAP。

为什么这很重要: 如果你将元素定位在其动画开始状态(屏幕外、缩放到 0、不透明度为 0)并将它们补间到你认为它们应该到达的位置,那么你是在猜测最终布局。重叠在视频渲染之前是不可见的。通过首先构建最终状态,你可以在添加任何运动之前看到并修复布局问题。

流程

  1. 为每个场景确定主角帧 — 大多数元素同时可见的时刻。这是你要构建的布局。
  2. 为该帧编写静态 CSS。.scene-content 容器必须使用 width: 100%; height: 100%; padding: Npx; 和 display: flex; flex-direction: column; gap: Npx; box-sizing: border-box 来完全填充场景。使用 padding 将内容向内推——永远不要在内容容器上使用 position: absolute; top: Npx。当内容高于剩余空间时,绝对定位的内容容器会溢出。仅将 position: absolute 保留给装饰性元素。
  3. 使用 gsap.from() 添加入场 — 从屏幕外/不可见动画到 CSS 位置。CSS 位置是基本事实;补间描述了到达那里的过程。
  4. 使用 gsap.to() 添加出场 — 从 CSS 位置动画到屏幕外/不可见。

示例

/* scene-content 填充场景,padding 定位内容 */
.scene-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 120px 160px;
  gap: 24px;
  box-sizing: border-box;
}
.title {
  font-size: 120px;
}
.subtitle {
  font-size: 42px;
}
/* 容器填充任何场景大小(1920x1080、1080x1920 等)。
   Padding 定位内容。Flex + gap 处理间距。 */

错误——硬编码尺寸和绝对定位:

.scene-content {
  position: absolute;
  top: 200px;
  left: 160px;
  width: 1920px;
  height: 1080px;
  display: flex; /* ... */
}
// 步骤 3:动画进入那些位置
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);

// 步骤 4:从那些位置动画退出
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);

当元素随时间共享空间时

如果元素 A 在元素 B 进入同一区域之前退出,则两者在各自的英雄帧中都应有正确的 CSS 位置。时间线排序保证它们永远不会在视觉上共存——但是如果你跳过了布局步骤,你将不会发现由于计时错误而导致的意外重叠情况。

什么算作有意重叠

分层效果(文本后的发光、阴影元素、背景图案)和 z 堆叠设计(卡片堆叠、深度层)是故意的。布局步骤是为了捕捉无意的重叠——两个标题相互叠加、统计数字覆盖标签、内容溢出画面边界。

数据属性

所有剪辑

属性必需值
id是唯一标识符
data-start是秒数或剪辑 ID 引用 ("el-1", "intro + 2")
data-duration对于 img/div/compositions 必需秒数。视频/音频默认为媒体持续时间。
data-track-index是整数。同一轨道的剪辑不能重叠。
data-media-start否源媒体的修剪偏移量(秒)
data-volume否0-1(默认 1)

data-track-index 不影响视觉分层——请使用 CSS z-index。

合成剪辑

属性必需值
data-composition-id是唯一的合成 ID
data-start是开始时间(根合成:使用 "0")
data-duration是优先于 GSAP 时间线持续时间
data-width / data-height是像素尺寸(1920x1080 或 1080x1920)
data-composition-src否外部 HTML 文件的路径

合成结构

通过 data-composition-src 加载的子合成使用 <template> 包装器。独立合成(主 index.html)不使用 <template> — 它们将 data-composition-id div 直接放在 <body> 中。在独立文件上使用 <template> 会向浏览器隐藏所有内容并破坏渲染。

子合成结构:

<template id="my-comp-template">
  <div data-composition-id="my-comp" data-width="1920" data-height="1080">
    <!-- 内容 -->
    <style>
      [data-composition-id="my-comp"] {
        /* 作用域样式 */
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
    <script>
      window.__timelines = window.__timelines || {};
      const tl = gsap.timeline({ paused: true });
      // 补间...
      window.__timelines["my-comp"] = tl;
    </script>
  </div>
</template>

在根目录中加载:<div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>

视频和音频

视频必须是 muted playsinline。音频始终是一个独立的 <audio> 元素:

<video
  id="el-v"
  data-start="0"
  data-duration="30"
  data-track-index="0"
  src="video.mp4"
  muted
  playsinline
></video>
<audio
  id="el-a"
  data-start="0"
  data-duration="30"
  data-track-index="2"
  src="video.mp4"
  data-volume="1"
></audio>

时间线契约

  • 所有时间线从 { paused: true } 开始 — 播放器控制播放
  • 注册每个时间线:window.__timelines["<composition-id>"] = tl
  • 框架自动嵌套子时间线 — 不要手动添加它们
  • 持续时间来自 data-duration,而不是 GSAP 时间线长度
  • 切勿创建空补间来设置持续时间

规则(不可协商)

确定性: 不要使用 Math.random()、Date.now() 或基于时间的逻辑。如果你需要伪随机值(例如 mulberry32),请使用种子 PRNG。

GSAP: 仅动画视觉属性(opacity、x、y、scale、rotation、color、backgroundColor、borderRadius、变换)。不要动画 visibility、display,也不要调用 video.play()/audio.play()。

动画冲突: 切勿同时从多个时间线动画同一元素的同一属性。

不要使用 repeat: -1: 无限重复的时间线会破坏捕获引擎。从合成持续时间计算确切的重复次数:repeat: Math.ceil(duration / cycleDuration) - 1。

同步时间线构建: 永远不要在 async/await、setTimeout 或 Promise 中构建时间线。捕获引擎在页面加载后同步读取 window.__timelines。字体由编译器嵌入,因此立即可用——无需等待字体加载。

绝对不要:

  1. 忘记注册 window.__timelines
  2. 使用视频作为音频——始终使用静音视频 + 独立的 <audio>
  3. 将视频嵌套在计时 div 内——使用非计时包装器
  4. 使用 data-layer(使用 data-track-index)或 data-end(使用 data-duration)
  5. 动画视频元素尺寸——动画一个包装器 div
  6. 在媒体上调用 play/pause/seek ——框架拥有回放控制权
  7. 创建没有 data-composition-id 的顶级容器
  8. 在任何时间线或补间上使用 repeat: -1 ——始终使用有限重复
  9. 异步构建时间线(在 async、setTimeout、Promise 内部)
  10. 在后续场景的剪辑元素上使用 gsap.set() ——它们在页面加载时不存在于 DOM 中。而在时间线内剪辑的 data-start 时间或之后使用 tl.set(selector, vars, timePosition)。
  11. 在内容文本中使用 <br> ——强制换行不考虑实际的渲染字体宽度。自然换行的文本加上 <br> 会产生额外的、不需要的换行,导致重叠。让文本通过 max-width 来换行。例外情况:每个单词刻意单独成行的简短显示标题(例如,130px 的“THE\nIMMORTAL\nGAME”)。

场景转场(不可协商)

每个多场景合成必须遵守以下所有规则。违反任何一条都会导致合成被破坏。

  1. 始终在场景之间使用转场。 没有生硬切换。没有例外。
  2. 始终在每个场景上使用入场动画。 每个元素通过 gsap.from() 动画进入。没有元素可以以完整形态出现。如果一个场景有 5 个元素,它需要 5 个入场补间。
  3. 除最终场景外,切勿使用出场动画。 这意味着:在转场触发之前,没有动画到不透明度为 0、屏幕外 y、缩放到 0 或任何其他“向外”动画的 gsap.to()。转场本身就是出场。离开场景的内容必须在转场开始的那一刻完全可见。
  4. 仅限最终场景: 最后一个场景可以使元素淡出(例如,淡出到黑色)。这是唯一允许 gsap.to(..., { opacity: 0 }) 的场景。

错误——在转场之前使用出场动画:

// 禁止——这会在转场可以使用该场景之前清空它
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// 转场在空帧上触发

正确——仅入场,转场处理出场:

// 场景 1 入场动画
tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7, ease: "power3.out" }, 0.3);
tl.from("#s1-subtitle", { y: 30, opacity: 0, duration: 0.5, ease: "power2.out" }, 0.6);
// 没有出场补间——7.2 秒处的转场处理场景更改
// 场景 2 入场动画
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);

动画护栏

  • 将第一个动画偏移 0.1-0.3 秒(而不是 t=0)
  • 在入场补间中变化缓动函数——每个场景至少使用 3 种不同的缓动函数
  • 不要在场景内重复入场模式
  • 避免在深色背景上使用全屏线性渐变(H.264 带状伪影——使用径向或纯色 + 局部发光)
  • 渲染视频使用 60px+ 的标题、20px+ 的正文、16px+ 的数据标签
  • 数字列上使用 font-variant-numeric: tabular-nums

当没有提供 visual-style.md 或动画方向时,请遵循 house-style.md 获取美学默认值。

排版和资源

  • 字体: 只需在 CSS 中编写你想要的 font-family — 编译器会自动嵌入支持的字体。如果字体不受支持,编译器会警告。
  • 为外部媒体添加 crossorigin="anonymous"
  • 对于动态文本溢出,使用 window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })
  • 所有文件位于项目根目录下,与 index.html 同级;子合成使用 ../

编辑现有合成

  • 首先读取完整合成 — 匹配现有字体、颜色、动画模式
  • 仅更改要求的内容
  • 保持无关剪辑的计时

输出清单

  • npx hyperframes lint 和 npx hyperframes validate 均通过
  • 处理对比度警告(请参阅下面的质量检查)
  • 验证动画编排(请参阅下面的质量检查)

质量检查

对比度

hyperframes validate 默认运行 WCAG 对比度审计。它选取 5 个时间点,截取页面屏幕截图,对每个文本元素后面的背景像素进行采样,并计算对比度。失败显示为警告:

⚠ WCAG AA 对比度警告 (3):
  · .subtitle "secondary text" — 2.67:1(需要 4.5:1,t=5.3s)

如果出现警告:

  • 在深色背景上:提高失败颜色的亮度,直到超过 4.5:1(正常文本)或 3:1(大文本,24px+ 或 19px+ 粗体)
  • 在浅色背景上:降低其亮度
  • 保持在调色板系列内 — 不要创造新颜色,调整现有颜色
  • 重新运行 hyperframes validate 直到干净

如果快速迭代并且稍后会检查,则使用 --no-contrast 跳过。

动画映射

在创作动画后,运行动画映射以验证编排:

npx hyperframes animation-map 生成每个时间线的时间线可视化,显示每个补间相对于时间线的开始和持续时间。使用它来:

  • 确认没有补间意外重叠在同一元素或属性上
  • 确保场景转场按预期对齐
  • 验证缓动函数的多样性
  • 检测所有元素是否都有入场动画

常见问题

  • 忘记添加 crossorigin="anonymous" 到 <video> 或 <audio> 元素会导致 CORS 错误——始终包含它。
  • 在子合成中使用 position: absolute 而不设置显式的 data-width 和 data-height 会破坏布局——始终在子合成上设置这些属性。
  • 在展示制作中,使用 data-volume="0.5" 来平衡音频级别,而不是手动调整增益。
  • 对于音频响应式动画,通过对音频文件进行 FFT 分析来确定节拍 BPM,然后使用 repeat: Math.ceil(duration / (60 / bpm)) - 1 创建与节拍同步的发光/脉冲动画。

支持的平台

此技能旨在与任何支持自定义技能或工具的 AI 代理平台一起使用,包括 Claude Code、Cursor 和 Codex。它假定安装了 HyperFrames CLI 工具链以进行 linting、预览和渲染。

相关技能