关于 framevideo
framevideo 是用于创作 HTML 视频合成的技能,通过定时片段、CSS 和动画时间线组织内容,指导创作者与编码 Agent 进行设计、动画和制作。
主要功能
- 01基于 HTML 构建合成
- 02使用 data 属性编排片段
- 03通过受支持的适配器制作动画
- 04创建多场景转场
- 05添加字幕和音频响应视觉效果
- 06通过变量参数化合成
适用场景
- 01使用 HTML 制作动画视频
- 02编排视频、音频和叠加片段
- 03构建带转场的多场景视频
- 04制作字幕或音频响应动画
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chanjing-ai/ framevideo --skill framevideo安装后在 Claude Code 中输入/framevideo确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
data-in-motion.mdhouse-style.mdpatterns.mdpalettes/bold-energetic.mdclean-corporate.mddark-premium.mdjewel-rich.mdmonochrome.mdnature-earth.mdneon-electric.mdpastel-soft.mdwarm-editorial.mdreferences/audio-reactive.mdbeat-direction.mdcaptions.mdcss-patterns.mddesign-picker.mddynamic-techniques.mdhtml-in-canvas-patterns.mdmotion-principles.mdnarration.mdprompt-expansion.mdtechniques.mdtext-effects.mdtranscript-guide.mdtransitions.mdreferences/transitions/catalog.mdcss-3d.mdcss-blur.mdcss-cover.md详细介绍
FrameVideo
HTML 是视频的真实来源。一个合成内容是一个 HTML 文件,包含用于计时的 data-* 属性、用于动画的 GSAP 时间轴,以及用于呈现外观的 CSS。框架负责片段可见性、媒体播放和时间轴同步。
适用场景
任何 FrameVideo 合成任务 都使用此技能。这是内容最多的技能(1100 多行)——使用下面的导航,快速找到所需内容。
🚀 初次使用 FrameVideo?
- 阅读“快速开始”(第 46 行)——最简可运行示例
- 阅读“核心概念”(第 95 行)——数据属性、计时、时间轴约定
- 尝试示例,然后返回查找特定功能
🎯 查找特定内容?
布局与结构:
- 安全区域与边距 → “布局与安全区域”(第 180 行)
- 多场景合成 → “场景转场”(第 450 行)[多场景必读]
- 合成架构 → “合成结构”(第 120 行)
动画与运动:
- 使用哪种动画库?→ “动画适配器路由”(第 28 行)
- GSAP 模式 → 加载
gsap技能(默认选项) - 音频响应式动画 → “音频响应式视觉效果”(第 620 行)
- 自定义效果 → 动画适配器技能(gsap、animejs、waapi 等)
媒体与素材:
- 视频/音频播放 → “视频与音频”(第 140 行)
- 字幕与说明文字 → “字幕”(第 580 行)
- 参数化合成 → “变量”(第 160 行)
工作流:
- AI 驱动的制作 → “AI 制作路线”(第 200 行)
- 质量检查 →
framevideo-visual-qa技能 - CLI 命令 →
framevideo-cli技能
请勿用于:
- CLI 命令(init、lint、preview、render)→
framevideo-cli技能 - 素材预处理(TTS、transcribe、bg-removal)→
framevideo-media技能 - 质量检查与验证 →
framevideo-visual-qa技能 - 禅境数字人 →
chanjing-digital-human技能
动画适配器路由
大多数 FrameVideo 合成动画默认使用 gsap。仅当内容或用户请求需要时,才使用特定的适配器技能:
| 用途 | 适配器技能 |
|---|---|
| 场景编排、文本/卡片运动、交错时间轴、大多数脚本化动画 | gsap |
| 用户指定 Anime.js,或移植精简的 Anime.js DOM/SVG 示例 | animejs |
| 简单的有限 CSS 关键帧、微光、辉光、遮罩和非序列化装饰 | css-animations |
不依赖外部库的轻量原生 element.animate() 运动 | waapi |
| 来自设计工具的现有 Lottie/dotLottie 素材 | lottie |
| 确定性的 3D 场景、GLTF、WebGL、镜头运动、着色器画面 | three |
| WebGPU/TypeGPU 着色器、粒子、液态玻璃、计算管线 | typegpu |
所有适配器均遵循同一规则:对渲染至关重要的动画必须具有确定性且可跳转。请勿在时间轴/实例中使用墙上时钟时间、无限循环或异步注册。
快速开始
最简可运行的合成内容:
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>My Video</title>
</head>
<body>
<div data-composition-id="main" data-width="1920" data-height="1080">
<div id="scene-1" class="clip" data-start="0" data-duration="5" data-track-index="1">
<div class="scene-content">
<h1>Hello FrameVideo</h1>
</div>
</div>
<style>
[data-composition-id="main"] { background: #000; color: #fff; }
.scene-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
box-sizing: border-box;
}
h1 { font-size: 120px; }
</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 });
tl.from("h1", { opacity: 0, y: 40, duration: 0.8, ease: "power3.out" }, 0.3);
window.__timelines["main"] = tl;
</script>
</div>
</body>
</html>
接下来:使用 npx framevideo lint 验证,使用 npx framevideo preview 预览。
章节索引
此技能包含 1100 多行,按以下主要章节组织。可跳转到所需章节:
🎬 核心概念(第 95-200 行)— 如果你刚接触 FrameVideo,请先阅读
- 数据属性与时间
- 合成结构
- 时间线约定
- 视频与音频
- 变量(参数化合成)
🎨 布局与设计(第 180-300 行)
- 安全区域与边距
- 排版与可读性
- 色彩与对比度
- 响应式布局
✨ 动画与运动(第 300-450 行)
- 动画适配器路由(见上文)
- GSAP 集成(或加载
gsap技能了解详情) - 时间与缓动
- 错开模式
🎞️ 场景转场(第 450-580 行)— 多场景视频必读
- 交叉淡化
- 擦除与显现
- 着色器转场
- 场景架构
📝 字幕(第 580-650 行)
- 字幕时间
- 样式与定位
- 转录文本集成
🎵 音频响应式视觉效果(第 620-720 行)
- 节拍检测
- 基于振幅的动画
- 音乐同步
🔧 高级模式(第 720-900 行)
- Canvas 中的 HTML 效果
- 子合成
- 动态内容
- 性能优化
📚 参考资料(第 900-1100 行)
- 示例集
- 故障排查
- 最佳实践
- API 参考
注意: 行号仅供参考。使用 Read 工具的 offset 参数跳转到相应章节;如果需要全面了解,也可以阅读整个文件。
核心概念
- 视频与音频元素
- 变量(参数化合成)
布局与安全区域(定位元素时展开):
- 先布局、后动画原则
- 容器模式
- 安全区域规则
- 文本处理
- 常见布局模式
动画与运动(添加动态效果时展开):
- 动画防护规则
- 进入/退出模式
- 错开与排序
- 场景节奏模板
- 动画冲突
场景转场(多场景视频必读):
- 4 条不可妥协的规则
- 转场实现
高级功能(按需查看):
- 字幕
- 音频响应式动画
- CSS 标记高亮
- 着色器转场
参考资料(深入了解):
- 参见末尾的“参考资料”章节,那里有 15 多篇详细指南
核心概念
数据属性与时间
每个剪辑都需要以下属性:
| 属性 | 必需 | 值 |
|---|---|---|
id | 是 | 唯一标识符 |
data-start | 是 | 秒数或剪辑 ID 引用("el-1"、"intro + 2") |
data-duration | 是* | 秒数。*视频/音频可选(使用媒体时长) |
data-track-index | 是 | 整数。同一轨道上的剪辑不能重叠 |
可选:
data-media-start- 源文件中的裁剪偏移量(秒)data-volume- 0-1(仅音频,默认值为 1)
重要: data-track-index 不控制视觉叠放顺序。请使用 CSS z-index。
合成结构
根元素必须包含以下属性:
<div data-composition-id="main" data-width="1920" data-height="1080">
</div>
独立合成(主 index.html):将根 <div> 直接放入 <body>。不要使用 <template>。
子合成(通过 data-composition-src 加载):必须使用 <template> 包装:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
</div>
</template>
时间线约定
5 条强制规则:
- 创建时暂停:
const tl = gsap.timeline({ paused: true }); - 注册时间线:
window.__timelines["main"] = tl;(键必须与data-composition-id匹配) - 同步构建: 切勿在
async、setTimeout或 Promise 中构建时间线 - 时长来自数据属性: 使用
data-duration,而不是 GSAP 时间线长度 - 框架自动嵌套: 不要手动添加子合成时间线
视频与音频
视频必须静音:
<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>
使用相同的源文件。播放由框架控制——切勿调用 .play()。
变量(参数化组合)
三步模式:
- 声明于
<html>根元素:
<html data-composition-variables='[
{"id":"title","type":"string","label":"Title","default":"Hello"}
]'>
- 在脚本中读取:
const { title } = window.__framevideo.getVariables();
document.getElementById("hero").textContent = title;
- 在渲染时覆盖:
npx framevideo render --variables '{"title":"Q4 Report"}'
变量类型:string、number、color、boolean、enum。
📐 布局与安全区域(点击展开)
AI 制作流程
当用户要求将创意、脚本、情节、分镜、参考图片或粗略的视频构想转化为生成视频时,将 framevideo-ai-production 作为工作流的前半部分调用。该技能会将创意素材转化为 Clip、Shot 和 Chanjing AIGC 计划。
如需使用 Chanjing AI 生成真实图片/视频:
- 使用
framevideo-ai-production生成 Shot 级别的 Chanjing AIGC 计划。 - 使用
chanjing-digital-human/references/ai-creation.md了解 Chanjing AI Creation 模型发现、幂等提交、短时同步轮询、下载和本地素材路径。 - 仅使用
assets/ai-creation/images/或assets/ai-creation/videos/下的本地素材进行合成。 - 返回此处构建 FrameVideo HTML 合成、音频、字幕、转场、QA、预览和渲染。
不要将 Chanjing 生成的远程输出 URL 直接插入合成 HTML。
方法
探索(仅适用于探索性请求)
对于开放式请求(“给我做一个产品发布视频”“为我们的品牌创作点内容”),如果用户还没有确定方向,请先了解其意图,再选择颜色:
- 受众 — 谁会观看?开发者?高管?普通消费者?
- 平台 — 视频在哪里播放?社交媒体(15 秒)、网站主视觉区、产品演示、内部使用?
- 优先事项 — 哪一点最重要?动态效果质量?内容准确性?品牌一致性?速度?
- 变体 — 用户想要多个选项,还是一个最佳镜头?
对于具体请求(“添加标题卡”“修正第 3 个场景的时间”),跳过探索环节。
对于探索性请求,可考虑提供 2–3 个差异显著的变体——不只是更换颜色,还可以采用不同的节奏、能量水平或结构方法。一个稳妥且符合预期,另一个大胆创新。无需强制提供——视情况使用即可。
第 1 步:设计系统
如果项目中已有设计规范,请先阅读。按优先级顺序查找:frame.md → design.md → DESIGN.md(在 Linux 上,design.md 和 DESIGN.md 是不同的文件——请检查这两种大小写;frame.md 始终使用小写,没有 FRAME.md 这个变体)。对于视频/framevideo 项目,frame.md 是首选规范;如果存在多个规范文件,则以它为准。它使用与 design.md 相同的格式。它是品牌颜色、字体和约束的唯一可信来源。请使用其中的确切值,不要臆造颜色或替换字体。任何格式都可以(YAML frontmatter、散文、表格——只需提取其中的值)。
如果规范中指定了你无法在本地找到的字体(没有包含 .woff2 文件的 fonts/ 目录,也不是内置字体),请在编写 HTML 前提醒用户:“规范指定了 [字体名称],但未找到字体文件。请将 .woff2 文件添加到 fonts/,否则我将使用最接近的内置替代字体 [字体名称]。”
如果不存在 frame.md 或 design.md,请让用户选择:
- 用户指定了风格或氛围? → 阅读 visual-styles.md,了解 8 种命名预设。选择最接近的风格。
- 想直观浏览选项? → 运行设计选择器:阅读 references/design-picker.md 了解完整工作流。这会提供一个可视化选择器页面。用户在浏览器中配置氛围、调色板、字体排版和动态效果,然后复制生成的 design.md 并粘贴回对话。
- 想跳过并快速开始? → 询问氛围、浅色或深色,以及是否有品牌颜色/字体。然后从 house-style.md 中选择调色板。
设计规范定义品牌,不定义视频构图规则。 视频构图规则见 references/video-composition.md 和 house-style.md。请以适合视频的尺度使用品牌颜色,不要使用网页 UI 的不透明度。
第 2 步:扩展提示词
每次创作都必须执行此步骤(单场景作品和简单编辑除外)。此步骤会结合设计规范(frame.md 或 design.md)和 house-style.md 梳理用户意图,并生成统一的中间内容,供所有下游 Agent 以相同方式读取。
阅读 references/prompt-expansion.md,了解完整流程和输出格式。
第 3 步:规划
编写 HTML 前,请先从高层次进行思考:
- 内容 — 观众应获得怎样的体验?确定叙事弧线、关键时刻和情绪节点。
- 结构 — 需要多少个构图,哪些是子构图,哪些是内联内容;哪些轨道承载什么内容(视频、音频、叠加层、字幕)。
- 节奏 — 实现前先确定场景节奏。哪些场景快速切入,哪些需要停留,着色器何时出现,能量何时达到峰值。为节奏模式命名:快-快-慢-快-着色器-停留。阅读 references/beat-direction.md,了解节奏模板。
- 时长 — 哪些片段决定时长,转场出现在哪里,节奏如何安排。
- 布局 — 先构建最终状态。参见下文“动画前先布局”。
- 动画 — 然后按照下方规则添加动态效果。
按要求构建。 用户要求“一个标题卡片”,并不意味着要求“一个标题卡片 + 3 个辅助场景 + 环境音乐 + 字幕”。每个场景、每个元素、每段补间动画都必须有存在的理由。如果增加场景或元素确实能改善作品,请提出建议,不要擅自添加。
对于小幅编辑(修复颜色、调整时间、添加一个元素),直接跳到规则部分。
编写任何构图 HTML 前,必须确认你已从第 1 步获得视觉识别方案。如果你打算使用 `#333`、`#3b82f6` 或 `Roboto`,说明你跳过了这一步。 ### 视觉 QA 技能创建新构图、进行大幅布局/字幕/素材/图层调整、修复视觉问题,或在宣布构图已达到视觉要求之前,请使用 framevideo-visual-qa 技能。该技能负责通用的视频 QA 规则,包括安全区域、文字与背景的对比度、文字溢出、意外重叠和运动碰撞。请将项目专属的品牌形象保留在 frame.md、design.md 或 DESIGN.md 中;通用视觉 QA 方法则保留在该技能中。
📐 布局与安全区域(点击展开)
先布局,后动画原则
先构建最终状态。 将每个元素放在它最清晰可见的时刻应处的位置——完全进入画面、位置正确,尚未开始退出。先将其编写为静态 HTML+CSS,暂时不要使用 GSAP。
原因: 如果你根据元素的动画起始状态(画面外、缩放为 0、透明度为 0)来定位,再将其动画到你认为的目标位置,那就只能靠猜。渲染前看不到重叠。先构建最终状态,就能在添加动态效果前发现并修复布局问题。
流程:
- 确定主视觉帧——大多数元素同时可见的时刻
- 为该帧编写静态 CSS
- 使用
gsap.from()添加入场动画——从画面外/不可见状态动画到 CSS 设定的位置 - 使用
gsap.to()添加退场动画——从 CSS 设定的位置动画到画面外/不可见状态
示例:
/* Step 1-2: Build the readable end state */
.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; }
// Step 3: Animate INTO those positions
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);
// Step 4: Animate OUT from those positions
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
容器模式
✅ 全场景容器(推荐):
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
padding: 120px 160px; /* Safe area */
gap: 24px;
box-sizing: border-box;
}
❌ 不要对内容使用绝对定位:
/* WRONG - breaks on long text */
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
}
position: absolute 仅用于装饰元素(粒子、光晕、背景形状)。
安全区域规则
关键信息应保持在安全区域内:
- 水平: 距离左右边缘约 5%(1920px 宽度时约为 96px)
- 垂直: 距离顶部/底部边缘约 5%(1080px 高度时约为 54px)
- 底部(字幕/CTA): 距离底部约 10%(约为 108px)
必须保持在安全区域内的内容:
- 关键信息、标题、正文
- Logo(除非有意锚定在边缘)
- CTA 按钮和链接
- 字幕
- 出镜者的脸
- 产品 UI 截图
- 法律/价格文案
可以超出安全区域的内容:
- 全出血背景
- 装饰性光晕和粒子
- 颗粒和纹理叠加层
- 转场效果
标记有意溢出的内容:<div class="grain" data-layout-allow-overflow>
文字处理
自然换行:
.headline {
max-width: 1200px; /* Wraps at this width */
font-size: 96px;
line-height: 1.1;
}
动态适配:
const { title } = window.__framevideo.getVariables();
const fontSize = window.__framevideo.fitTextFontSize(title, {
maxWidth: 1200,
fontFamily: 'Inter',
fontWeight: 700
});
document.getElementById('title').style.fontSize = fontSize + 'px';
避免使用固定尺寸的文字容器——改用带内边距的弹性容器。
常见布局模式
居中标题卡片:
<div class="scene-content">
<h1>Main Message</h1>
<p class="subtitle">Supporting detail</p>
</div>
<style>
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 32px;
box-sizing: border-box;
}
</style>
分栏布局(文字 + 视觉元素):
<div class="scene-content">
<div class="text-side">
<h2>Product Feature</h2>
<p>Explanation</p>
</div>
<div class="visual-side">
<img src="assets/product.png" />
</div>
</div>
<style>
.scene-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 80px;
align-items: center;
width: 100%;
height: 100%;
padding: 120px 160px;
box-sizing: border-box;
}
</style>
🎬 动画与动态效果(点击展开)
动画规范
时间偏移——首次动画绝不能从 t=0 开始:
// ❌ Wrong
tl.from(".title", { opacity: 0 }, 0);
// ✅ Right
tl.from(".title", { opacity: 0 }, 0.2); // 0.1-0.3s offset
改变缓动曲线——每个场景至少使用 3 种不同的缓动效果:
tl.from("#el1", { opacity: 0, ease: "power3.out" }, 0.2);
tl.from("#el2", { opacity: 0, ease: "expo.out" }, 0.4);
tl.from("#el3", { opacity: 0, ease: "back.out(1.4)" }, 0.6);
常见缓动效果:power1.out、power2.out、power3.out、expo.out、back.out(1.7)、elastic.out(1, 0.3)
不要重复使用入场模式——每个元素都应采用独特的方向或样式:
// ❌ Wrong - everything from top
tl.from("#title", { y: 60, opacity: 0 }, 0.2);
tl.from("#subtitle", { y: 60, opacity: 0 }, 0.5);
// ✅ Right - varied directions
tl.from("#title", { y: 60, opacity: 0 }, 0.2);
tl.from("#subtitle", { x: -30, opacity: 0 }, 0.5);
tl.from("#cta", { scale: 0.8, opacity: 0 }, 0.8);
性能属性——优先使用 GPU 加速的属性:
- ✅ 快:
opacity、x、y、scale、rotation - ❌ 慢:
width、height、top、left、margin、padding、font-size
常见动画模式
错开入场:
// Stagger by time
tl.from(".item", {
y: 30,
opacity: 0,
duration: 0.5,
stagger: 0.1,
ease: "power2.out"
}, 0.5);
// Stagger from center
tl.from(".grid-item", {
scale: 0.8,
opacity: 0,
duration: 0.4,
stagger: { amount: 0.6, from: "center" },
ease: "back.out(1.4)"
}, 1.0);
依次播放与重叠播放:
// Sequence - one after another
tl.from("#el1", { opacity: 0, duration: 0.5 }, 0);
tl.from("#el2", { opacity: 0, duration: 0.5 }, ">"); // After previous ends
// Overlap - start before previous ends
tl.from("#el1", { opacity: 0, duration: 0.8 }, 0);
tl.from("#el2", { opacity: 0, duration: 0.5 }, "<0.4"); // 0.4s after el1 starts
基于标签的序列编排:
tl.addLabel("intro", 0);
tl.from("#title", { opacity: 0 }, "intro");
tl.from("#subtitle", { opacity: 0 }, "intro+=0.5");
tl.addLabel("content", 2.5);
tl.from("#feature1", { x: -40, opacity: 0 }, "content");
场景节奏模板
实现之前先确定节奏:
- 快-快-慢: 快速开场节拍 → 留出时间传达信息(2s、3s、5s)
- 铺垫-高潮-收尾: 能量逐渐提升 → 达到高潮 → 平稳收尾(3s、2s、4s)
- 均匀脉冲: 节奏一致(每个节拍均为 3–4s,适用于教学内容)
更多模板请参阅 references/beat-direction.md。
切勿在多个时间线上对同一元素的同一属性制作动画:
// ❌ Wrong - both animate opacity
timeline1.to("#el", { opacity: 0.5 });
timeline2.to("#el", { opacity: 1 }); // Conflict!
// ✅ Right - separate properties
timeline1.to("#el", { opacity: 0.5 });
timeline2.to("#el", { x: 100 }); // No conflict
场景过渡(不可妥协)
每个多场景合成都必须遵循以下 4 条规则:
规则 1:始终使用过渡
场景之间不得硬切。每次场景切换都必须使用过渡。
规则 2:始终使用入场动画
每个场景中的每个元素都必须通过 gsap.from() 动画入场。不得让任何元素以完整形态直接出现。
规则 3:绝不使用退场动画(最后一个场景除外)
过渡开始前不要让元素动画退场。过渡本身就是退场。过渡开始时,离场场景的内容必须完全可见。
规则 4:仅限最后一个场景
只有最后一个场景允许使用 gsap.to(..., { opacity: 0 }) 或退场动画。
示例:
// Scene 1 (0-7s)
// ✅ Entrance animations only
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);
// ❌ NO exit animations - transition handles it
// Transition at 7s (see references/transitions.md)
// Scene 2 (8-15s)
// ✅ Entrance animations
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
// Final scene (15-20s)
// ✅ OK to fade out on final scene only
tl.from("#s3-cta", { scale: 0.9, opacity: 0, duration: 0.5 }, 15.2);
tl.to("#s3-cta", { opacity: 0, duration: 0.5, ease: "power2.in" }, 19.5);
过渡实现请参阅 references/transitions.md。
动画前先布局
.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;
}
/* Container fills any scene size (1920x1080, 1080x1920, etc).
Padding positions content. Flex + gap handles spacing. */
错误 — 尺寸硬编码并使用绝对定位:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Step 3: Animate INTO those positions
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);
// Step 4: Animate OUT from those positions
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-variable-values | 否 | 子合成宿主元素上针对每个实例的变量覆盖值的 JSON 对象 |
在根 <html> 元素上:
| 属性 | 必填 | 值 |
|---|---|---|
data-composition-variables | 否 | 已声明变量的 JSON 数组(id/type/label/default)——驱动 Studio 编辑 UI,并为 getVariables() 提供默认值 |
合成结构
通过 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"] {
/* scoped styles */
}
</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 });
// tweens...
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>
变量(参数化合成)
无需编辑源 HTML,即可使用不同内容渲染同一合成,例如标题、主题颜色、价格和字幕。
三步模式:
- 声明:在合成的
<html>根元素上使用data-composition-variables声明变量。每个条目都需要id、type(取值为string、number、color、boolean、enum之一)、label和default。枚举条目还需要options: [{value, label}, ...]。 - 读取:在合成脚本中使用
window.__framevideo.getVariables()读取解析后的值。返回已声明的默认值、每个实例的覆盖值和 CLI 覆盖值合并后的结果。 - 覆盖:在渲染时使用
npx framevideo render --variables '{...}'(顶层覆盖),或在宿主元素上使用data-variable-values='{...}'(为子合成的每个实例分别覆盖)。
<!doctype html>
<html
data-composition-variables='[
{"id":"title","type":"string","label":"Title","default":"Hello"},
{"id":"theme","type":"enum","label":"Theme","default":"light","options":[
{"value":"light","label":"Light"},
{"value":"dark","label":"Dark"}
]}
]'
>
<body>
<div data-composition-id="root" data-width="1920" data-height="1080">
<h1 id="hero" class="clip" data-start="0" data-duration="3"></h1>
<script>
const { title, theme } = window.__framevideo.getVariables();
document.getElementById("hero").textContent = title;
document.body.dataset.theme = theme;
</script>
</div>
</body>
</html>
# Dev preview uses declared defaults
npx framevideo preview
# Render with overrides
npx framevideo render --variables '{"title":"Q4 Report","theme":"dark"}' --output q4.mp4
# Or from a JSON file
npx framevideo render --variables-file ./vars.json
**子合成的每实例值:**通过 data-composition-src 加载的子合成中也可以使用相同的 getVariables()。每个宿主元素都会传递自己的值:
<div
data-composition-id="card-pro"
data-composition-src="compositions/card.html"
data-variable-values='{"title":"Pro","price":"$29"}'
></div>
<div
data-composition-id="card-enterprise"
data-composition-src="compositions/card.html"
data-variable-values='{"title":"Enterprise","price":"Custom"}'
></div>
运行时会在每个实例中,将各宿主元素的 data-variable-values 覆盖值应用于子合成已声明的默认值,因此可以多次嵌入同一源,并使用不同内容。
实用规则:
- 始终为每个已声明变量提供合理的
default。开发预览会使用默认值——如果没有默认值,合成在提供--variables前将无法正确渲染。 - 在脚本顶部读取一次变量(
const { title } = ...),不要在帧循环或事件处理程序中读取——getVariables()每次调用都会分配一个新对象。 - 在 CI 中使用
--strict-variables,以便在遇到未声明的键或类型不匹配时快速失败。 - 变量类型会在渲染时验证。
string、number、boolean和color(十六进制字符串)会检查typeof;enum会检查值是否在已声明的options中。
视频和音频
视频必须设置为 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>
对于 Chanjing 平台的背景音乐,请先使用 CLI 下载本地资源:
npx framevideo chanjing music list --compact
npx framevideo chanjing music download --id <music-id> --chorus --duration 10 --json
使用返回的 htmlSnippet,或手动编写相同结构。BGM 应保存在本地的 assets/music/ 下;不要在合成 HTML 中直接引用 Chanjing/OSS 远程 URL。建议将 BGM 音量设为 0.10-0.22;存在语音或数字人旁白时,使用 data-volume="0.12"。
对于 Chanjing 平台的音效,请先下载 SFX 资源,并将其放置在事件发生的时间点:
npx framevideo chanjing sound-effect list --compact
npx framevideo chanjing sfx download --id <effect-id> --volume 0.8 --json
SFX 应保存在本地的 assets/sfx/ 下。使用简短且与事件对应的 <audio> 片段,将 data-start 设为准确的提示时间,将 data-track-index="30" 或更高,并根据混音将 data-volume="0.6-1" 设为适当值。
时间线约定
- 所有时间线均以
{ paused: true }开始——播放由播放器控制 - 注册每条时间线:
window.__timelines["<composition-id>"] = tl - 框架会自动嵌套子时间线——不要手动添加
- 时长取自
data-duration,而非 GSAP 时间线长度 - 不要创建空补间来设置时长
规则(不可协商)
确定性: 不要使用 Math.random()、Date.now() 或基于时间的逻辑。若需要伪随机值,请使用带种子的 PRNG(例如 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。编译器会嵌入字体,因此字体可立即使用——无需等待字体加载。
绝对不要:
- 忘记注册
window.__timelines - 使用视频播放音频——始终使用静音视频 + 单独的
<audio> - 将视频嵌套在定时 div 中——请使用非定时包装元素
- 使用
data-layer(使用data-track-index)或data-end(使用data-duration) - 对视频元素尺寸进行动画处理——请对包装 div 进行动画处理
- 对媒体调用播放/暂停/定位操作——播放由框架控制
- 创建缺少
data-composition-id的顶层容器 - 在任何时间线或补间中使用
repeat: -1——始终使用有限次数的重复 - 异步构建时间线(在
async、setTimeout、Promise中) - 在后续场景中对片段元素使用
gsap.set()——页面加载时这些元素尚未存在于 DOM 中。请改为在时间线中、片段的data-start时间点或之后使用tl.set(selector, vars, timePosition)。 - 在内容文本中使用
<br>——强制换行不会考虑实际渲染的字体宽度。自然换行的文本加上<br>会产生多余换行,导致重叠。请改用max-width让文本自然换行。例外:每个词都刻意单独占一行的简短展示标题(例如,130px 字号的 “THE\nIMMORTAL\nGAME”)。
场景转场(不可妥协)
每个多场景合成都 MUST 遵循以下所有规则。违反其中任何一条,合成都不合格。
- 始终在场景之间使用转场。 不得跳切。没有例外。
- 始终为每个场景使用入场动画。 每个元素都必须通过
gsap.from()动画入场。不得让任何元素以完整形态直接出现。如果一个场景有 5 个元素,就需要 5 个入场补间动画。 - 绝不使用退场动画,最后一个场景除外。这意味着:在转场触发之前,不得使用任何让透明度变为 0、让元素沿 y 轴移出屏幕、让缩放变为 0,或执行其他“退场”动画的
gsap.to()。转场本身就是退场。转场开始时,离场场景的内容 MUST 完全可见。 - 仅限最后一个场景: 最后一个场景可以淡出元素(例如,淡变为黑屏)。这是唯一允许使用
gsap.to(..., { opacity: 0 })的场景。
错误 — 转场前使用退场动画:
// BANNED — this empties the scene before the transition can use it
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// transition fires on empty frame
正确 — 仅使用入场动画,由转场处理退场:
// Scene 1 entrance animations
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);
// NO exit tweens — transition at 7.2s handles the scene change
// Scene 2 entrance animations
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
如果不存在 frame.md 或 design.md,请遵循 house-style.md 中的美学默认规范。
字体排版与资源
- 内置字体: 在 CSS 中填写所需的
font-family— 编译器会自动嵌入受支持的字体。 - 自定义字体: 如果规范(
frame.md或design.md)指定了非内置字体,用户必须在fonts/目录中提供.woff2文件。如果缺少文件,请在编写 HTML 前发出警告。文件存在时,添加指向本地文件的@font-face声明。 - 为外部媒体添加
crossorigin="anonymous" - 对于动态文本溢出,请使用
window.__framevideo.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight }) - 所有文件都放在项目根目录,与
index.html同级;子合成使用../
编辑现有合成
- 读取实际文件,不要猜测。 编辑、扩展或创建配套合成时,请读取现有源文件。不要凭记忆还原十六进制颜色代码。不要猜测 GSAP 缓动模式。合成本身就是规范 — 从中提取精确值。
- 根据读取到的内容匹配现有字体、颜色和动画模式
- 只修改用户要求的内容
- 保留无关片段的时间安排
输出检查清单
创作期间的 Chanjing OAuth
当请求的视频需要 Chanjing 支持的功能(公共语音、平台语音、数字人,或任何 Studio 面板提示缺少 Chanjing 身份验证时),请调用 chanjing-digital-human 技能 — 尤其是 Auth Contract 和 Missing Credentials UX。如果可以通过预览或 Studio 登录,不要只提供终端环境变量的指导就停止。
切勿打印、回显或提交令牌或凭据存储内容。缺少身份验证时,不要伪造生成的资源;只有用户接受临时方案时,才可使用占位内容继续。
输出检查清单
快速检查(立即运行,并等待结果):
-
npx framevideo lint和npx framevideo validate均通过 - 如果存在设计规范(
frame.md或design.md),则确认设计规范已遵循
较慢的检查(向用户展示预览时并行运行):
-
npx framevideo inspect通过,或每个报告的溢出都已明确标记为有意保留 - 已处理对比度警告(见下方的质量检查)
- 已验证动画编排(见下方的质量检查)
- 已对新建合成和重大视觉更改应用
framevideo-visual-qa
质量检查
视觉检查
framevideo inspect 会在无头 Chrome 中运行合成,逐帧检查时间线,并通过时间戳、选择器、边界框和修复提示来定位视觉布局问题。请在运行 lint 和 validate 后使用:
npx framevideo inspect
npx framevideo inspect --json
失败通常意味着文本溢出了气泡或卡片、固定尺寸的标签裁切了动态文案,或文本移出了画布。可通过增大容器尺寸或内边距、减小字号或字间距、设置实际的 max-width 让文本在容器内换行,或对动态文案使用 window.__framevideo.fitTextFontSize(...) 来修复。
对于内容密集的视频,可使用 --samples 15;对于指定的关键帧,可使用 --at 1.5,4,7.25。默认会折叠重复出现的静态问题,以免 Agent 上下文被大量内容淹没。如果溢出是入场或退场动画的预期效果,请为元素或其祖先添加 data-layout-allow-overflow。如果某个装饰元素不应参与审查,请为其添加 data-layout-ignore。
framevideo layout 是同一项检查的兼容别名。
对比度
framevideo validate 默认会运行 WCAG 对比度审计。它会跳转到 5 个时间戳,为页面截图,采样每个文本元素背后的背景像素,并计算对比度。未通过的项目会显示为警告:
⚠ WCAG AA contrast warnings (3):
· .subtitle "secondary text" — 2.67:1 (need 4.5:1, t=5.3s)
如果出现警告:
- 深色背景:调亮未通过检查的颜色,直到达到 4.5:1(普通文本)或 3:1(大号文本,24px 及以上,或 19px 及以上的粗体文本)
- 浅色背景:调暗颜色
- 保持在同一调色板范围内——不要引入新颜色,而应调整现有颜色
- 反复运行
framevideo validate,直到没有警告
如果需要快速迭代,之后再检查,可使用 --no-contrast 跳过检查。
设计规范遵循情况
如果存在设计规范(frame.md 或 design.md),完成创作后请确认合成内容符合规范。阅读 HTML 并检查:
- 颜色——合成内容中的每个十六进制颜色值都应出现在规范的调色板部分(无论用户将其标为“颜色”“调色板”“主题”等)。标记任何自行引入的颜色。
- 字体排印——字体系列和字重应与规范中的字体要求一致,不得替换。
- 圆角——如果规范指定了圆角样式,边框圆角值应与之匹配。
- 间距——如果规范指定了密度范围,内边距和间隙值应在该范围内。
- 层次——阴影的使用应符合规范中声明的层次级别(扁平 = 无阴影,微妙 = 浅阴影,分层 = 光晕)。
- 规避规则——如果规范列出了应避免的事项(通常称为“绝对不要做”“禁忌”“反模式”或“应该做与不应该做”),请确认这些事项都未出现。
将违规项整理成检查清单。开始提供服务前,请逐一修复。
如果没有设计规范(仅遵循品牌风格指南的路径),请检查:
- 调色板一致性——所有场景使用相同的背景色、前景色和强调色,不得为单个场景自行引入颜色。
- 避免偷懒套用默认值——对照 house-style.md 中的“需要审慎考虑的偷懒默认值”列表检查合成内容。如有任何默认值,都必须是基于内容有意选择的,而不是直接套用。
动画映射
编写动画后,运行动画映射以验证动画编排:
node skills/framevideo/scripts/animation-map.mjs <composition-dir> \
--out <composition-dir>/.framevideo/anim-map
输出单个 animation-map.json,其中包含:
- 每个补间动画的摘要:
"#card1 animates opacity+y over 0.50s. moves 23px up. fades in. ends at (120, 200)" - ASCII 时间线:显示整个合成时长内所有补间动画的甘特图
- 错开动画检测:报告实际间隔(
"3 elements stagger at 120ms") - 空白区间:超过 1s 且没有动画的时段——是有意停顿,还是缺少入场动画?
- 元素生命周期:首次/最后动画时间、最终可见性
- 场景快照:5 个关键时间戳处可见元素的状态
- 标记:
offscreen、collision、invisible、paced-fast(短于 0.2s)、paced-slow(长于 2s)
阅读 JSON。检查摘要中是否有异常。检查每个标记,并修正或说明原因。确认时间线体现了预期的动画编排节奏。修正后重新运行。
小幅编辑时跳过(例如修正颜色、调整一个持续时间)。在新建合成和进行重大动画更改时运行。
参考资料(按需加载)
framevideo-visual-qaskill — 针对安全区域、重叠、溢出、对比度、动态文本适配和运动碰撞进行视觉 QA。用于新构图或重大布局、字幕、素材变更后。- references/captions.md — 与音频同步的字幕、歌词和卡拉 OK。根据语气自适应检测样式、逐词设置样式、防止文本溢出、确保字幕退出、分组单词。添加任何与音频时间同步的文本时阅读。
- references/audio-reactive.md — 音频响应动画:将频段和振幅映射到 GSAP 属性。需要视觉效果响应音乐、语音或声音时阅读。
- references/css-patterns.md — CSS+GSAP 标记高亮:高亮、画圈、爆发、涂鸦、描摹。确定性强且完全可寻址。为文本添加视觉强调时阅读。
- references/video-composition.md — 视频媒介规则:密度、色彩呈现、缩放、画面构图,以及将设计规范视为品牌而非布局。始终阅读——这些规则优先于 Web 设计直觉。
- references/beat-direction.md — 节拍规划:概念、情绪、编排动词、节奏模板、转场决策、景深层次。多场景构图时务必阅读。
- references/typography.md — 排版:字体搭配、OpenType 特性、深色背景调整、字体发现脚本。始终阅读——每个构图都有文本。
- references/motion-principles.md — 动态设计原则、图像动态处理、关键 GSAP 规则。始终阅读——每个构图都有动态效果。
- references/techniques.md — 13 种基础动画技术及其代码模式:SVG 绘制、Canvas 2D、CSS 3D、动态排版、Lottie、视频合成、打字效果、可变字体、MotionPath、速度转场、音频响应、clip-path 揭示、WebGL 着色器。请改编这些模式,不要直接复制粘贴。(预构建 UI 模板——终端外观、设备模型、情绪板布局——请参见
registry/blocks/。) - references/html-in-canvas-patterns.md — HTML-in-Canvas 模式:通过
drawElementImage+layoutsubtree将实时 DOM 用作 GPU 纹理。包含通用样板代码和约 6 种效果配方(iPhone/MacBook 模型、液态玻璃、磁吸、传送门、碎裂、文本光标)。每段视频使用 1–3 个主视觉节拍。 - references/narration.md — 节奏、语气、脚本结构、数字读法、开场白模式。构图包含画外音或 TTS 时阅读。
- references/design-picker.md — 通过可视化选择器创建 design.md。不存在
frame.md或design.md且用户想要创建其中一个时阅读。 - visual-styles.md — 8 种命名视觉风格,包含十六进制调色板、GSAP 缓动特征和着色器搭配。用户指定风格或需要生成设计规范时阅读。
- house-style.md — 未指定
frame.md或design.md时的默认动态效果、尺寸和调色板。 - patterns.md — PiP、标题卡、幻灯片模式。
- data-in-motion.md — 数据、统计和信息图模式。
- references/transcript-guide.md — 字幕侧的转录处理:输入格式、强制质量检查、JS 清理、OpenAI/Groq API 回退方案、“不存在转录文本时”的处理流程。(关于
transcribeCLI 调用、模型选择规则和.en陷阱,请参见framevideo-mediaskill。) - references/dynamic-techniques.md — 动态字幕动画技术(卡拉 OK、clip-path、猛击、散射、弹性、3D)。
- references/transitions.md — 场景转场:交叉淡化、擦除、显现、着色器转场。能量感/氛围选择、CSS 与 WebGL 指南。多场景合成时务必阅读——没有转场的场景会显得像跳切。
- transitions/catalog.md — 硬性规则、场景模板,以及各类型实现代码的索引。
- 着色器转场位于
@framevideo/shader-transitions(packages/shader-transitions/)中——请阅读软件包源代码,而非技能文件。
GSAP 模式和效果位于 /gsap 技能中。