gitframes-compose
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesegitframes-compose
gitframes-compose
Setup
设置
Import everything from the npm package. If the project doesn't have gitframes set up yet, follow the skill first (install, project scaffold, first render).
gitframesgitframes从 npm 包导入所有内容。如果项目尚未配置 gitframes,请先按照 skill 操作(安装、项目脚手架、首次渲染)。
gitframesgitframesOverview
概述
gitframesEvery composition is defined as a tree of layout nodes compiled into a deterministic execution program: .
(composition, frame) → pixelsgitframes每个合成均被定义为布局节点树,并编译为确定性的执行程序:。
(composition, frame) → pixelsArchitecture & Core Concepts
架构与核心概念
1. Composition Lifecycle
1. 合成生命周期
A instance defines canvas dimensions, frame rate, background, registered fonts, global audio tracks, and the layer hierarchy:
Compositiontypescript
import { Composition, Layer, FontManager } from "gitframes";
// Always register fonts before assembling layers
await FontManager.register({
family: "Inter",
source: "assets/fonts/Inter.ttf"
});
const comp = new Composition({
width: 1920,
height: 1080,
fps: 30,
durationMs: 10000, // or computeDuration()
backgroundColor: "#0E0D0C",
fonts: ["assets/fonts/Inter.ttf"],
});
comp.add(Layer.box({ /* ... */ }));
comp.add(Layer.audio("assets/score.mp3", { volume: 1 }));Compositiontypescript
import { Composition, Layer, FontManager } from "gitframes";
// Always register fonts before assembling layers
await FontManager.register({
family: "Inter",
source: "assets/fonts/Inter.ttf"
});
const comp = new Composition({
width: 1920,
height: 1080,
fps: 30,
durationMs: 10000, // or computeDuration()
backgroundColor: "#0E0D0C",
fonts: ["assets/fonts/Inter.ttf"],
});
comp.add(Layer.box({ /* ... */ }));
comp.add(Layer.audio("assets/score.mp3", { volume: 1 }));2. Scene Architecture (Production Pattern)
2. 场景架构(生产模式)
For commercials, films, and multi-shot videos, break the project into discrete, modular scene functions using scene-local frames:
- Tokens first (): Centralize color palette, typography, easing curves, and a tempo-locked beat grid (
theme.ts). Never use arbitrary margins or ad-hoc hex codes.BEAT = Math.round(fps * 60 / BPM) - Scene modules (): Each scene returns a full-frame root box with
scenes/hero.tsandstartFrame. Children position themselves relative to scene start (durationFrames= scene onset).frame: 0 - Master assembly (): Imports scenes, mounts them to
film.ts, attaches the master soundtrack and composition-level grading/grain.comp.add(scene())
typescript
// theme.ts
export const W = 1920;
export const H = 1080;
export const FPS = 30;
export const BEAT = 18; // 100 BPM at 30 fps
export const beats = (n: number) => Math.round(n * BEAT);
export const INK = "#0E0D0C";
export const PAPER = "#EEE9E0";
export const EMBER = "#FF5A1F";
export function scene(options: { id: string; from: number; to: number; children: unknown[] }) {
return Layer.box({
id: options.id,
position: "absolute",
x: 0,
y: 0,
width: W,
height: H,
startFrame: options.from,
durationFrames: options.to - options.from,
children: options.children as never,
});
}对于广告、电影和多镜头视频,应使用场景局部帧将项目拆分为独立的模块化场景函数:
- **设计令牌优先():**集中管理调色板、字体排印、缓动曲线以及与节拍锁定的节拍网格(
theme.ts)。切勿使用任意边距或临时拼凑的十六进制颜色值。BEAT = Math.round(fps * 60 / BPM) - **场景模块():**每个场景返回一个全屏根框,其中包含
scenes/hero.ts和startFrame。子节点相对于场景开始位置进行定位(durationFrames= 场景开始)。frame: 0 - **主合成():**导入场景,将它们挂载到
film.ts,并附加主音轨以及合成级别的调色/颗粒效果。comp.add(scene())
typescript
// theme.ts
export const W = 1920;
export const H = 1080;
export const FPS = 30;
export const BEAT = 18; // 100 BPM at 30 fps
export const beats = (n: number) => Math.round(n * BEAT);
export const INK = "#0E0D0C";
export const PAPER = "#EEE9E0";
export const EMBER = "#FF5A1F";
export function scene(options: { id: string; from: number; to: number; children: unknown[] }) {
return Layer.box({
id: options.id,
position: "absolute",
x: 0,
y: 0,
width: W,
height: H,
startFrame: options.from,
durationFrames: options.to - options.from,
children: options.children as never,
});
}Layer Primitives (Layer.*
)
Layer.*图层基础组件(Layer.*
)
Layer.*All builders return instances with chainable , , and effect methods.
Layer.*AnimatableNode<T>.animate().apply()| Builder | Node | Description & Key Options |
|---|---|---|
| | Slug GPU vector text. Options: |
| | Rectangular container / card. Options: |
| | Auto-layout container (Yoga). Options: |
| | Video layer decoded via mediabunny. Options: |
| | Static raster image ( |
| | Vector SVG asset. Options: |
| | Lottie vector animation file. |
| | Audio track layer. Options: |
| | Parametric vector graphics ( |
| | SRT-driven synchronized subtitle engine. Bottom-anchored auto-wrapping cues. |
| | Charts built from native nodes (d3 scales and geometry). |
| | 3D scene camera with FOV, lens rack focus, and orbital coordinates. |
| | 3D lighting primitive ( |
| | 3D mesh loader ( |
所有 构造器都会返回 实例,并提供可链式调用的 、 和效果方法。
Layer.*AnimatableNode<T>.animate().apply()| 构造器 | 节点 | 说明与关键选项 |
|---|---|---|
| | Slug GPU 矢量文本。选项: |
| | 矩形容器/卡片。选项: |
| | 自动布局容器(Yoga)。选项: |
| | 通过 mediabunny 解码的视频层。选项: |
| | 静态栅格图像( |
| | 矢量 SVG 资源。选项: |
| | Lottie 矢量动画文件。 |
| | 音频轨道层。选项: |
| | 参数化矢量图形( |
| | 由 SRT 驱动的同步字幕引擎。字幕底部锚定并自动换行。 |
| | 由原生节点构建的图表(d3 比例尺与几何图形)。 |
| | 3D 场景摄像机,支持 FOV、镜头拉焦和轨道坐标。 |
| | 3D 光照图元( |
| | 3D 网格加载器( |
Animation System (LayerAnimation
)
LayerAnimation动画系统(LayerAnimation
)
LayerAnimationCreate animations using or fluent factory methods. Animations are composed of keyframed tracks or procedural drivers.
LayerAnimation.create()使用 或流式工厂方法创建动画。动画由关键帧轨道或程序化驱动器组成。
LayerAnimation.create()1. Keyframe Transitions (fromTo
)
fromTo1. 关键帧过渡(fromTo
)
fromTotypescript
import { LayerAnimation } from "gitframes";
// Fluent chain
const anim = LayerAnimation.create()
.fadeIn(0, 20, "power2.out")
.fromTo("y", 60, 0, { start: 0, end: 24, ease: "expo.out" })
.fromTo("scale", 0.95, 1.0, { start: 0, end: 30, ease: "power3.out" });
// Easing bases: none, power1, power2, power3, sine, circ, expo, back, elastic, bounce, spring, cubic, hold
// Directions: in, out, inOut (e.g. "expo.out", "back.out(1.7)", "power2.inOut")For more than two keyframes on one prop, list them with : each tuple is , where the ease shapes the motion into that keyframe. Frames are rounded, so beat math () can go straight in:
keys[frame, value, ease?]at + BEAT / 2typescript
const pop = LayerAnimation.create()
.keys("scale", [[0, 0], [10, 1.12, "back.out(1.6)"], [16, 1, "power2.out"]])
.keys("opacity", [[0, 0], [6, 1]]);
// Static form: LayerAnimation.keys("rotation", [[0, -8], [20, 0, "expo.out"]])Keyframes past the layer's never play; the render warns () and reports it.
durationFramesanimation_truncatedrenderVideo({ qa: true })typescript
import { LayerAnimation } from "gitframes";
// Fluent chain
const anim = LayerAnimation.create()
.fadeIn(0, 20, "power2.out")
.fromTo("y", 60, 0, { start: 0, end: 24, ease: "expo.out" })
.fromTo("scale", 0.95, 1.0, { start: 0, end: 30, ease: "power3.out" });
// Easing bases: none, power1, power2, power3, sine, circ, expo, back, elastic, bounce, spring, cubic, hold
// Directions: in, out, inOut (e.g. "expo.out", "back.out(1.7)", "power2.inOut")缓动基准:none、power1、power2、power3、sine、circ、expo、back、elastic、bounce、spring、cubic、hold。
方向:in、out、inOut(例如 、、)。
"expo.out""back.out(1.7)""power2.inOut"如果一个属性需要两个以上的关键帧,请使用 列出:每个元组为 ,其中 ease 决定运动进入该关键帧时的形态。帧数会四舍五入,因此节拍计算()可以直接使用:
keys[frame, value, ease?]at + BEAT / 2typescript
const pop = LayerAnimation.create()
.keys("scale", [[0, 0], [10, 1.12, "back.out(1.6)"], [16, 1, "power2.out"]])
.keys("opacity", [[0, 0], [6, 1]]);
// Static form: LayerAnimation.keys("rotation", [[0, -8], [20, 0, "expo.out"]])超过图层 的关键帧永远不会播放;渲染器会发出警告(),并且 会报告该问题。
durationFramesanimation_truncatedrenderVideo({ qa: true })2. Kinetic Typography (TextAnimator
)
TextAnimator2. 动态排版(TextAnimator
)
TextAnimatorText animators operate at the GPU glyph instancing level, cascading offsets across characters, words, or lines:
typescript
import { Layer, LayerAnimation, TextAnimator } from "gitframes";
// Letter-by-letter wave rise with perspective tilt and blur
const headlineNode = Layer.text("PRECISION MOTION", {
fontSize: 96,
fontFamily: "Inter",
fill: "#FFFFFF",
animators: [
TextAnimator.waveRise({
y: 40,
rotationX: 45,
opacity: 0,
blur: 10,
easing: "expo.in"
})
]
}).animate(
// Sweep the selection offset across the text string
LayerAnimation.create().kineticSweep(-1, 1, 0, 24, "power2.inOut")
);Presets available: , , , , .
TextAnimator.waveRise()TextAnimator.wordPop()TextAnimator.blurIn()TextAnimator.flip3D()TextAnimator.colorSweep()文本动画器在 GPU 字形实例化层级运行,可在字符、单词或行之间产生级联偏移:
typescript
import { Layer, LayerAnimation, TextAnimator } from "gitframes";
// Letter-by-letter wave rise with perspective tilt and blur
const headlineNode = Layer.text("PRECISION MOTION", {
fontSize: 96,
fontFamily: "Inter",
fill: "#FFFFFF",
animators: [
TextAnimator.waveRise({
y: 40,
rotationX: 45,
opacity: 0,
blur: 10,
easing: "expo.in"
})
]
}).animate(
// Sweep the selection offset across the text string
LayerAnimation.create().kineticSweep(-1, 1, 0, 24, "power2.inOut")
);可用预设:、、、、。
TextAnimator.waveRise()TextAnimator.wordPop()TextAnimator.blurIn()TextAnimator.flip3D()TextAnimator.colorSweep()3. 3D Spatial Transforms & Camera Motion
3. 3D 空间变换与摄像机运动
Layers can be transformed in 3D perspective space:
typescript
const card = Layer.box({
width: 600,
height: 400,
background: "#181715",
borderRadius: 24,
transformStyle: "preserve-3d",
perspective: 1200,
}).animate(
LayerAnimation.create()
.fromTo("rotateX", 25, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("rotateY", -35, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("translateZ", -200, 0, { start: 0, end: 30, ease: "expo.out" })
);For full 3D camera staging, use :
CameraAnimationtypescript
import { CameraAnimation } from "gitframes";
const camAnim = CameraAnimation.camera()
.orbit({
azimuth: { from: -45, to: 15 },
elevation: { from: 10, to: 25 },
radius: { from: 2000, to: 1400 },
start: 0,
end: 90,
ease: "sine.inOut"
})
.rackFocus({
fromDistance: 2000,
toDistance: 1400,
fromFStop: 1.8,
toFStop: 4.0,
start: 20,
end: 60
});图层可以在 3D 透视空间中进行变换:
typescript
const card = Layer.box({
width: 600,
height: 400,
background: "#181715",
borderRadius: 24,
transformStyle: "preserve-3d",
perspective: 1200,
}).animate(
LayerAnimation.create()
.fromTo("rotateX", 25, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("rotateY", -35, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("translateZ", -200, 0, { start: 0, end: 30, ease: "expo.out" })
);如需完整的 3D 摄像机编排,请使用 :
CameraAnimationtypescript
import { CameraAnimation } from "gitframes";
const camAnim = CameraAnimation.camera()
.orbit({
azimuth: { from: -45, to: 15 },
elevation: { from: 10, to: 25 },
radius: { from: 2000, to: 1400 },
start: 0,
end: 90,
ease: "sine.inOut"
})
.rackFocus({
fromDistance: 2000,
toDistance: 1400,
fromFStop: 1.8,
toFStop: 4.0,
start: 20,
end: 60
});4. Procedural & Audio-Reactive Drivers
4. 程序化与音频响应驱动器
Properties can be bound to Perlin wiggles, harmonic springs, or audio-extracted signals:
typescript
// Organic floating drift
const floatAnim = LayerAnimation.create()
.wiggle("x", { frequency: 1.5, amplitude: 25 })
.wiggle("y", { frequency: 1.2, amplitude: 20 });
// Physics spring overshoot
const springAnim = LayerAnimation.create()
.spring("scale", { damping: 14, stiffness: 190, mass: 1 });
// Audio reactivity: pass the signal object straight in…
const kick = Signal.fromArray(kickEnvelope, fps); // or Signal.builder({ type: "custom", fn })
const beatAnim = LayerAnimation.create()
.signal("scale", kick, { multiplier: 0.12, offset: 1.0, smoothing: 2 });
// …or register it once by name and bind the name (handy when many layers share it)
comp.addSignal("drum_stem_signal", kick);
const beatAnim2 = LayerAnimation.create()
.signal("opacity", "drum_stem_signal", { multiplier: 0.5, offset: 0.5 });.signal().colorSignal()value × multiplier + offsetinline_signal_Ncomp.addSignalundefinedctx.frame属性可以绑定到 Perlin 抖动、谐波弹簧或从音频中提取的信号:
typescript
// Organic floating drift
const floatAnim = LayerAnimation.create()
.wiggle("x", { frequency: 1.5, amplitude: 25 })
.wiggle("y", { frequency: 1.2, amplitude: 20 });
// Physics spring overshoot
const springAnim = LayerAnimation.create()
.spring("scale", { damping: 14, stiffness: 190, mass: 1 });
// Audio reactivity: pass the signal object straight in…
const kick = Signal.fromArray(kickEnvelope, fps); // or Signal.builder({ type: "custom", fn })
const beatAnim = LayerAnimation.create()
.signal("scale", kick, { multiplier: 0.12, offset: 1.0, smoothing: 2 });
// …or register it once by name and bind the name (handy when many layers share it)
comp.addSignal("drum_stem_signal", kick);
const beatAnim2 = LayerAnimation.create()
.signal("opacity", "drum_stem_signal", { multiplier: 0.5, offset: 0.5 });.signal().colorSignal()value × multiplier + offsetinline_signal_Ncomp.addSignalundefinedctx.frameCritical Authoring Rules & Gotchas
关键创作规则与易错点
- Local Keyframe Coordinates: Keyframes are strictly relative to the node's own timeline window (= node appearance). Never offset keyframe frames by the parent's
0.startFrame - Coordinate Overrides: Animating overrides the node's layout
y. If a node is placed aty, animatey: 400, NOTfromTo("y", 440, 400).fromTo("y", 40, 0) - No String Styles for Borders: Use and
borderColor. CSSborderWidthis silently ignored.border: "1px solid red" - Font Registration is Mandatory: In headless WebGPU, system fonts do not exist. Every font must be registered via and listed in
FontManager.register().comp.fonts - Integer Frames Only: Keyframe frames, start frames, and durations must be integers. Decimal frames cause schema validation errors.
- Relative vs. Absolute: Providing or
xwithoutyturns the node into an absolute-positioned layer removed from flex flow.position: "relative" - Audio Layer Duration: Every must be assigned
Layer.audio()to prevent audio truncation in headless rendering.durationFrames - Premultiplied Alpha: Alpha shaders operate with premultiplied alpha. Never use factor in custom blends.
"src-alpha"
- **局部关键帧坐标:**关键帧严格相对于节点自身的时间轴窗口(= 节点出现)。绝不要根据父节点的
0偏移关键帧帧号。startFrame - **坐标覆盖:**动画化 会覆盖节点的布局
y。如果节点放置在y,请使用y: 400,而不是fromTo("y", 440, 400)。fromTo("y", 40, 0) - **边框不能使用字符串样式:**使用 和
borderColor。CSSborderWidth会被静默忽略。border: "1px solid red" - **字体注册是强制要求:**在无头 WebGPU 环境中不存在系统字体。每个字体都必须通过 注册,并列在
FontManager.register()中。comp.fonts - **仅允许整数帧:**关键帧帧号、开始帧和持续时长必须为整数。小数帧会导致模式验证错误。
- **相对与绝对定位:**提供 或
x但未设置y时,节点会变成脱离 flex 流式布局的绝对定位图层。position: "relative" - **音频图层时长:**每个 都必须指定
Layer.audio(),以防止无头渲染时音频被截断。durationFrames - **预乘 Alpha:**Alpha 着色器使用预乘 Alpha。自定义混合中切勿使用 因子。
"src-alpha"