compose-video

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Compose Video

视频合成

HTML is the source of truth for video. A composition is an HTML file with
data-*
attributes for timing, a GSAP timeline for animation, and CSS for appearance. The framework handles clip visibility, media playback, and timeline sync.
HTML是视频的核心依据。一个合成内容是包含用于时间控制的
data-*
属性、用于动画的GSAP时间线以及用于外观的CSS的HTML文件。该框架负责处理剪辑可见性、媒体播放和时间线同步。

Approach

方法步骤

Before writing HTML, think at a high level:
  1. What — what should the viewer experience? Identify the narrative arc, key moments, and emotional beats.
  2. Structure — how many compositions, which are sub-compositions vs inline, what tracks carry what (video, audio, overlays, captions).
  3. Timing — which clips drive the duration, where do transitions land, what's the pacing.
  4. Execute — then implement using the rules below.
For small edits (fix a color, adjust timing, add one element), skip straight to the rules.
When no
visual-style.md
or animation direction is provided, follow house-style.md for motion defaults, sizing, and color palettes.
在编写HTML之前,先从宏观层面规划:
  1. 内容定位 — 观众应该获得怎样的体验?明确叙事脉络、关键节点和情感节奏。
  2. 结构设计 — 需要多少个合成内容,哪些是子合成内容、哪些是内联内容,不同轨道分别承载什么(视频、音频、叠加层、字幕)。
  3. 时间规划 — 哪些剪辑决定时长,转场位置在哪里,整体节奏如何。
  4. 执行实现 — 然后按照以下规则进行开发。
对于小型修改(调整颜色、修改时间、添加单个元素),可以直接跳过规划步骤,直接遵循规则。
如果没有提供
visual-style.md
或动画指导,请遵循house-style.md中的动效默认设置、尺寸规范和调色板要求。

Data Attributes

数据属性

All Clips

所有剪辑

AttributeRequiredValues
id
YesUnique identifier
data-start
YesSeconds or clip ID reference (
"el-1"
,
"intro + 2"
)
data-duration
Required for img/div/compositionsSeconds. Video/audio defaults to media duration.
data-track-index
YesInteger. Same-track clips cannot overlap.
data-media-start
NoTrim offset into source (seconds)
data-volume
No0-1 (default 1)
data-track-index
does not affect visual layering — use CSS
z-index
.
属性是否必填取值
id
唯一标识符
data-start
秒数或剪辑ID引用(如
"el-1"
,
"intro + 2"
data-duration
图片/ div/合成内容必填秒数。视频/音频默认使用媒体自身时长。
data-track-index
整数。同一轨道的剪辑不能重叠
data-media-start
源媒体的裁剪偏移量(秒)
data-volume
0-1(默认值为1)
data-track-index
不影响视觉层级 — 请使用CSS的
z-index
属性控制。

Composition Clips

合成内容剪辑

AttributeRequiredValues
data-composition-id
YesUnique composition ID
data-duration
YesTakes precedence over GSAP timeline duration
data-width
/
data-height
YesPixel dimensions (1920x1080 or 1080x1920)
data-composition-src
NoPath to external HTML file
属性是否必填取值
data-composition-id
唯一的合成内容ID
data-duration
优先级高于GSAP时间线的时长
data-width
/
data-height
像素尺寸(1920x1080 或 1080x1920)
data-composition-src
外部HTML文件路径

Composition Structure

合成内容结构

Every composition is a
<template>
wrapping a
<div>
with
data-composition-id
. Each must include its own GSAP script and register its timeline:
html
<template id="my-comp-template">
  <div data-composition-id="my-comp" data-width="1920" data-height="1080">
    <!-- content -->
    <style>
      [data-composition-id="my-comp"] {
        /* scoped styles */
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/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>
Load in root:
<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>
每个合成内容都是一个包裹着带有
data-composition-id
属性的
<div>
<template>
。每个合成内容必须包含自己的GSAP脚本并注册其时间线:
html
<template id="my-comp-template">
  <div data-composition-id="my-comp" data-width="1920" data-height="1080">
    <!-- content -->
    <style>
      [data-composition-id="my-comp"] {
        /* scoped styles */
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/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>

Video and Audio

视频与音频

Video must be
muted playsinline
. Audio is always a separate
<audio>
element:
html
<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>
视频必须添加
muted playsinline
属性。音频始终作为独立的
<audio>
元素存在:
html
<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>

Timeline Contract

时间线约定

  • All timelines start
    { paused: true }
    — the player controls playback
  • Register every timeline:
    window.__timelines["<composition-id>"] = tl
  • Framework auto-nests sub-timelines — do NOT manually add them
  • Duration comes from
    data-duration
    , not from GSAP timeline length
  • Never create empty tweens to set duration
  • 所有时间线初始状态为
    { paused: true }
    — 由播放器控制播放
  • 必须注册每个时间线:
    window.__timelines["<composition-id>"] = tl
  • 框架会自动嵌套子时间线 — 请勿手动添加
  • 时长由
    data-duration
    决定,而非GSAP时间线的长度
  • 不要创建空补间动画来设置时长

Rules (Non-Negotiable)

必须遵守的规则

Deterministic: No
Math.random()
,
Date.now()
, or time-based logic. The renderer must produce identical output every time.
GSAP: Only animate visual properties (
opacity
,
x
,
y
,
scale
,
rotation
,
color
,
backgroundColor
,
borderRadius
, transforms). Do NOT animate
visibility
,
display
, or call
video.play()
/
audio.play()
.
Animation conflicts: Never animate the same property on the same element from multiple timelines simultaneously — causes flickering in headless renders.
Never do:
  1. Forget
    window.__timelines
    registration
  2. Use video for audio — always muted video + separate
    <audio>
  3. Nest video inside a timed div — use a non-timed wrapper
  4. Use
    data-layer
    (use
    data-track-index
    ) or
    data-end
    (use
    data-duration
    )
  5. Animate video element dimensions — animate a wrapper div
  6. Call play/pause/seek on media — framework owns playback
  7. Create a top-level container without
    data-composition-id
确定性: 禁止使用
Math.random()
Date.now()
或基于时间的逻辑。渲染器每次必须生成完全相同的输出。
GSAP: 仅允许动画视觉属性(
opacity
x
y
scale
rotation
color
backgroundColor
borderRadius
、变换属性)。禁止动画
visibility
display
属性,或调用
video.play()
/
audio.play()
方法。
动画冲突: 禁止同时从多个时间线动画同一元素的同一属性 — 这会导致无头渲染时出现闪烁问题。
绝对禁止的操作:
  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
    的顶级容器

Editing Existing Compositions

编辑现有合成内容

  • Read the full composition first — match existing fonts, colors, animation patterns
  • Only change what was requested — don't rewrite untouched sections
  • Don't rewrite entire files for small changes
  • Preserve timing of unrelated clips
  • 先完整阅读现有合成内容 — 匹配现有的字体、颜色、动画模式
  • 仅修改被要求更改的部分 — 不要重写未涉及的内容
  • 不要为了小修改而重写整个文件
  • 保留无关剪辑的时间设置

Typography and Assets

排版与资源

  • Every composition loads its own fonts (
    @import
    or
    @font-face
    in
    <style>
    )
  • Use
    font-display: block
    for local fonts — renderer needs fonts loaded before capturing
  • Add
    crossorigin="anonymous"
    to media loaded from external URLs
  • Minimum readable text: 20px landscape, 18px portrait
  • All files (video, audio, fonts, images) live at the project root alongside
    index.html
  • From sub-compositions, use
    ../
    to reference root files
For PiP, title cards, and slide show patterns, see patterns.md. For data, stats, and infographics, see data-in-motion.md.
  • 每个合成内容都要加载自己的字体(在
    <style>
    中使用
    @import
    @font-face
  • 本地字体使用
    font-display: block
    — 渲染器需要在捕获画面前加载完成字体
  • 从外部URL加载的媒体需添加
    crossorigin="anonymous"
    属性
  • 最小可读文本尺寸:横版20px,竖版18px
  • 所有文件(视频、音频、字体、图片)都存放在项目根目录,与
    index.html
    同级
  • 从子合成内容中引用根目录文件时使用
    ../
    路径
关于画中画、标题卡片和幻灯片模式,请参考patterns.md。 关于数据、统计信息和信息图,请参考data-in-motion.md

Output Checklist

输出检查清单

  • Every top-level container has
    data-composition-id
  • Every composition has
    data-width
    ,
    data-height
    ,
    data-duration
  • Compositions in own HTML files, loaded via
    data-composition-src
  • <template>
    wrapper on sub-compositions
  • window.__timelines
    registered for every composition
  • 100% deterministic — no randomness
  • Each composition includes GSAP:
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
  • 每个顶级容器都带有
    data-composition-id
    属性
  • 每个合成内容都带有
    data-width
    data-height
    data-duration
    属性
  • 合成内容存放在独立的HTML文件中,通过
    data-composition-src
    加载
  • 子合成内容使用
    <template>
    包裹
  • 每个合成内容都已注册
    window.__timelines
  • 100%确定性 — 无随机逻辑
  • 每个合成内容都包含GSAP:
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>