Communitygithub.com

ilya-makarov-dev/Reframe

在 HyperFrames HTML 中创建视频合成、动画、标题卡、叠加层、字幕、配音、音频响应式视觉和场景过渡。适用于构建任何基于 HTML 的视频内容、添加与音频同步的字幕、生成文本转语音旁白、创建音频响应式动画(节拍同步、发光、脉冲由音乐驱动)、添加动画文本高亮(标记扫描、手绘圆圈、爆炸线、涂鸦、素描轮廓)或在场景之间添加过渡(淡入淡出、擦除、揭示、着色器过渡)。涵盖合成创作、时间线编排、媒体处理及完整的视频制作工作流程。关于 CLI 命令(init、lint、preview、render、transcribe、tts),请参阅 hyperframes-cli 技能。

Reframe 是什么?

Reframe 是一个专为 HyperFrames 框架设计的 AI 助手技能,用于在 HTML 中创建专业级的视频合成、动画和过渡效果。它将 HTML 作为视频的单一事实来源,通过 `data-*` 属性控制时间线、GSAP 处理动画、CSS 控制外观,从而完全掌控剪辑可见性、媒体播放和时间线同步。该技能支持多种视频制作需求,包括:创建合成与子合成、添加字幕和配音(文本转语音)、实现音频响应式动画(如节拍同步的发光和脉冲效果)、添加动画文本高亮(如标记扫描、手绘圆圈、爆炸线、涂鸦和素描轮廓),以及在场景之间应用各种过渡(淡入淡出、擦除、揭示、着色器过渡)。 该技能强调严格的视觉身份前置流程,要求在任何合成编写之前,必须通过 DESIGN.md、visual-style.md 或用户明确指导定义颜色、字体和动效规则。同时,它强制推行“布局先于动画”的方法,要求先用静态 CSS 定位每个元素在最佳可见帧的位置,再使用 `gsap.from()` 添加进入动画,避免了传统动画开发中常见的布局重叠问题。非协商规则包括:禁止使用 `Math.random()` 等非确定性逻辑、禁止无限重复时间线、禁止异步构建时间线,以及始终在场景间使用过渡而非跳跃剪辑。这些规则确保了生成的视频合成组件具有确定性、可预测性和高性能的渲染效果。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ilya-makarov-dev/Reframe/tree/HEAD/.claude/skills/hyperframes

在你喜欢的 AI 中提问

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

预览

来自技能 README

文档

HyperFrames

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

方法

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

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

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

视觉身份门禁

按此顺序检查:

  1. 项目中存在 DESIGN.md? → 读取它。使用其中确切的颜色、字体、动效规则和“不要做”的约束。
  2. 存在 visual-style.md? → 读取它。应用其中的 style_prompt_full 和结构化字段。(注意:visual-style.md 是项目特定文件。visual-styles.md 是包含 8 个命名预设的样式库 — 不同的文件。)
  3. 用户命名了一个样式(例如,“Swiss Pulse”、“黑暗科技风”、“奢侈品牌”)? → 读取 visual-styles.md 以获取 8 个命名预设。生成一个最小的 DESIGN.md,包含:## Style Prompt(一段落)、## Colors(3-5 个十六进制值及其角色)、## Typography(1-2 个字体族)、## What NOT to Do(3-5 个反模式)。
  4. 以上都不存在? → 在编写任何 HTML 之前先问 3 个问题:
    • 情绪是什么?(爆炸性/电影感/流畅/技术性/混乱/温暖)
    • 浅色还是深色画布?
    • 是否有特定的品牌颜色、字体或视觉参考? 然后根据答案生成一个最小的 DESIGN.md。

每个合成必须将其调色板和字体追溯到 DESIGN.md、visual-style.md 或明确的用户指示。如果你正在使用 #333、#3b82f6 或 Roboto — 你跳过了这一步骤。

对于动效默认值、尺寸、进入模式和缓动 — 请遵循 house-style.md。house style 处理事物如何移动。DESIGN.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 来填充整个场景。使用内边距将内容向内推 — 绝不要对内容容器使用 position: absolute; top: Npx。绝对定位的内容容器在内容高于剩余空间时会溢出。将 position: absolute 仅保留给装饰元素。
  3. 使用 gsap.from() 添加进入动画 — 从屏幕外/不可见动画到 CSS 位置。CSS 位置是基本事实;补间描述了到达那里的过程。
  4. 使用 gsap.to() 添加退出动画 — 从 CSS 位置动画到屏幕外/不可见。

示例

/* scene-content 填充场景,内边距定位内容 */
.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 等)。
   内边距定位内容。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/合成是必需的秒数。视频/音频默认为媒体时长。
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)。

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. 永远不要使用退出动画,最后一个场景除外。这意味着:在过渡触发之前,没有 gsap.to() 将透明度动画化为 0、将 Y 轴动画到屏幕外、将缩放动画化为 0,或任何其他“退出”动画。过渡本身就是退出。正在离开的场景的内容在过渡开始时必须完全可见。
  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"
  • 对于

…

[为翻译而截断]

相关技能