remotion-bits
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRemotion Bits
Remotion Bits
Animation components and utilities for building Remotion videos. The library's most powerful feature is Scene3D — a camera-based 3D presentation system (like impress.js) that enables cinematic multi-section compositions with flying camera moves, step-aware element animations, and Transform3D position management.
When building any non-trivial composition, prefer Scene3D as your foundation. It handles camera movement, timing, element positioning, and responsive layout all in one system. Individual components (AnimatedText, Particles, etc.) work best as content placed inside Scene3D steps.
Prerequisites: >= 4.0, >= 18, >= 18
remotionreactreact-dombash
undefined用于构建Remotion视频的动画组件与工具集。该库最强大的功能是Scene3D——一个基于相机的3D展示系统(类似impress.js),可实现带有飞行动画镜头、步骤感知元素动画和Transform3D位置管理的电影级多段合成内容。
**在构建任何非简单合成内容时,优先选择Scene3D作为基础框架。**它一站式处理镜头移动、时间控制、元素定位和响应式布局。单个组件(如AnimatedText、Particles等)作为内容放置在Scene3D的步骤中使用效果最佳。
前置要求: >= 4.0, >= 18, >= 18
remotionreactreact-dombash
undefinedVia npm
Via npm
npm install remotion-bits
npm install remotion-bits
Via jsrepo (copies source for customization)
Via jsrepo (copies source for customization)
npx jsrepo init https://unpkg.com/remotion-bits/registry.json
npx jsrepo add animated-text particle-system scene-3d
undefinednpx jsrepo init https://unpkg.com/remotion-bits/registry.json
npx jsrepo add animated-text particle-system scene-3d
undefinedImports
导入方式
Everything is exported from :
remotion-bitstsx
import {
// 3D Scene System (primary workflow)
Scene3D, Step, Element3D, StepResponsive,
Transform3D, Vector3,
useScene3D, useCamera, useActiveStep,
// Animation Components
AnimatedText, AnimatedCounter, TypeWriter, CodeBlock,
StaggeredMotion, GradientTransition, MatrixRain, ScrollingColumns,
Particles, Spawner, Behavior,
// Core
useViewportRect,
interpolate, Easing,
interpolateColorKeyframes, interpolateGradientKeyframes,
random, resolvePoint, createRect,
} from "remotion-bits";所有功能均从导出:
remotion-bitstsx
import {
// 3D场景系统(核心工作流)
Scene3D, Step, Element3D, StepResponsive,
Transform3D, Vector3,
useScene3D, useCamera, useActiveStep,
// 动画组件
AnimatedText, AnimatedCounter, TypeWriter, CodeBlock,
StaggeredMotion, GradientTransition, MatrixRain, ScrollingColumns,
Particles, Spawner, Behavior,
// 核心工具
useViewportRect,
interpolate, Easing,
interpolateColorKeyframes, interpolateGradientKeyframes,
random, resolvePoint, createRect,
} from "remotion-bits";Core Concepts
核心概念
1. AnimatedValue
1. AnimatedValue
Most animation properties accept : a static number OR an array of keyframes interpolated over the animation's duration.
AnimatedValuetsx
opacity: 1 // Static
opacity: [0, 1] // Animate 0→1
opacity: [0, 1, 0.5, 0] // Multi-keyframe: 0→1→0.5→0 evenly spaced
scale: [0.8, 1] // Scale from 80% to 100%
y: [30, 0] // Slide up from 30px offsetKeyframes are evenly distributed across the duration. With 4 keyframes over 60 frames: frame 0→20→40→60.
大多数动画属性支持:可以是静态数值,也可以是随动画时长插值的关键帧数组。
AnimatedValuetsx
opacity: 1 // 静态值
opacity: [0, 1] // 从0动画到1
opacity: [0, 1, 0.5, 0] // 多关键帧:0→1→0.5→0,均匀分布
scale: [0.8, 1] // 从80%缩放至100%
y: [30, 0] // 从下方30px位置向上滑动关键帧在动画时长内均匀分布。例如60帧动画包含4个关键帧时,会在第0、20、40、60帧触发关键帧变化。
2. Responsive Sizing with useViewportRect
2. 使用useViewportRect实现响应式尺寸
Never hardcode pixel values. Always use viewport-relative units:
tsx
const rect = useViewportRect();
// rect.width — composition width (e.g. 1920)
// rect.height — composition height (e.g. 1080)
// rect.vw — 1% of width (19.2)
// rect.vh — 1% of height (10.8)
// rect.vmin — min(vw, vh) — USE THIS for most sizing
// rect.vmax — max(vw, vh)
// rect.cx, cy — center coordinates
const { vmin } = rect;Use for font sizes, element dimensions, spacing, and padding. This ensures compositions render identically at 1920×1080, 1080×1920, 3840×2160, etc.
vmin**绝对不要硬编码像素值。**始终使用视口相对单位:
tsx
const rect = useViewportRect();
// rect.width — 合成内容宽度(如1920)
// rect.height — 合成内容高度(如1080)
// rect.vw — 宽度的1%(19.2)
// rect.vh — 高度的1%(10.8)
// rect.vmin — vw和vh中的较小值 — 大多数尺寸设置优先使用这个
// rect.vmax — vw和vh中的较大值
// rect.cx, cy — 中心坐标
const { vmin } = rect;使用设置字体大小、元素尺寸、间距和内边距。这能确保合成内容在1920×1080、1080×1920、3840×2160等不同分辨率下渲染效果一致。
vminUnderstanding vmin at Common Resolutions
常见分辨率下的vmin理解
vminmin(width, height) / 100| Composition Size | Aspect Ratio | vmin (px) | vmin * 8 | vmin * 10 | vmin * 5 |
|---|---|---|---|---|---|
| 1920×1080 | 16:9 landscape | 10.8 | 86px | 108px | 54px |
| 1080×1920 | 9:16 portrait | 10.8 | 86px | 108px | 54px |
| 1080×1080 | 1:1 square | 10.8 | 86px | 108px | 54px |
| 3840×2160 | 16:9 4K | 21.6 | 173px | 216px | 108px |
| 1280×720 | 16:9 720p | 7.2 | 58px | 72px | 36px |
vminmin(width, height) / 100| 合成尺寸 | 宽高比 | vmin(像素) | vmin * 8 | vmin * 10 | vmin * 5 |
|---|---|---|---|---|---|
| 1920×1080 | 16:9 横屏 | 10.8 | 86px | 108px | 54px |
| 1080×1920 | 9:16 竖屏 | 10.8 | 86px | 108px | 54px |
| 1080×1080 | 1:1 正方形 | 10.8 | 86px | 108px | 54px |
| 3840×2160 | 16:9 4K | 21.6 | 173px | 216px | 108px |
| 1280×720 | 16:9 720p | 7.2 | 58px | 72px | 36px |
Font Size Reference (at 1920×1080, vmin = 10.8px)
字体大小参考(1920×1080分辨率下,vmin=10.8px)
Use these proven multipliers from production bits. Err on the side of LARGER, not smaller.
| Role | Multiplier | Pixels at 1080p | Example |
|---|---|---|---|
| Hero / main title | | 108–162px | Full-screen headline |
| Section heading | | 65–86px | Scene3D step titles |
| Subheading | | 43–54px | Card titles, counters |
| Body / card label | | 27–32px | Feature labels, descriptions |
| Code / small text | | 16–22px | Code blocks, captions |
| Fine print | | 11–13px | Code font in dense blocks |
Common mistake: using for headings. At 1080p that's only 32px — fine for body text but too small for any heading. For prominent headings, start at minimum.
vmin * 3vmin * 8使用经过生产验证的乘数。优先选择更大的数值,而非更小。
| 用途 | 乘数 | 1080p下的像素值 | 示例 |
|---|---|---|---|
| 主标题/大标题 | | 108–162px | 全屏标题 |
| 章节标题 | | 65–86px | Scene3D步骤标题 |
| 副标题 | | 43–54px | 卡片标题、计数器 |
| 正文/卡片标签 | | 27–32px | 功能标签、描述文本 |
| 代码/小字 | | 16–22px | 代码块、说明文字 |
| 极小文字 | | 11–13px | 密集代码块中的字体 |
常见错误: 使用设置标题。在1080p下这仅为32px——适合正文但完全不适合标题。重要标题至少从开始设置。
vmin * 3vmin * 8Aspect Ratio Awareness
宽高比适配
The aspect ratio determines which dimension is the "min" for vmin:
- Landscape (16:9, 1920×1080): is based on height (1080/100 = 10.8). Horizontal space is abundant; vertical space is limited.
vmin - Portrait (9:16, 1080×1920): is based on width (1080/100 = 10.8). Vertical space is abundant; horizontal space is limited.
vmin - Square (1:1, 1080×1080): . All directions equal.
vmin = vmax = vh = vw
Layout implications:
- In landscape, full-width text can be very long — consider or
width: vmin * 70constraintsmaxWidth - In portrait, titles may need to wrap — use fewer words or smaller multipliers for width-constrained text
- Use for sizes that should scale uniformly regardless of orientation
vmin - Use /
vwwhen sizing should follow a specific axis (e.g., full-width background:vh)rect.width
宽高比决定了vmin是基于宽度还是高度计算:
- 横屏(16:9,1920×1080): 基于高度计算(1080/100=10.8)。水平空间充足,垂直空间有限。
vmin - 竖屏(9:16,1080×1920): 基于宽度计算(1080/100=10.8)。垂直空间充足,水平空间有限。
vmin - 正方形(1:1,1080×1080): 。所有方向空间一致。
vmin = vmax = vh = vw
布局注意事项:
- 横屏模式下,全屏文本可能过长——考虑设置或
width: vmin * 70限制maxWidth - 竖屏模式下,标题可能需要换行——减少字数或使用更小的宽度乘数
- 使用设置需要随方向均匀缩放的尺寸
vmin - 当尺寸需要跟随特定轴时使用/
vw(例如全屏背景:vh)rect.width
Element Size Reference
元素尺寸参考
| Element | Multiplier | Description |
|---|---|---|
| Card width | | Floating card, info panel |
| Card height | | Match content needs |
| Card border-radius | | Subtle rounding |
| Spacing/gap | | Between elements |
| Padding | | Inside containers |
| Particle size | | Individual particle dot |
| Icon size | | Decorative icons |
| 元素 | 乘数 | 描述 |
|---|---|---|
| 卡片宽度 | | 悬浮卡片、信息面板 |
| 卡片高度 | | 根据内容需求调整 |
| 卡片圆角 | | 轻微圆角效果 |
| 元素间距 | | 元素之间的间隔 |
| 内边距 | | 容器内部填充 |
| 粒子大小 | | 单个粒子点 |
| 图标大小 | | 装饰性图标 |
3. Transition Props Pattern
3. 过渡属性模式
Components accept a prop with this shape:
transitiontsx
transition={{
// Timing
duration: 30, // Duration in frames
delay: 10, // Delay before start
frames: [0, 60], // Or explicit frame range (alternative to duration)
easing: "easeOutCubic", // Easing curve
// Transform (all accept AnimatedValue)
x: [30, 0], // translateX
y: [30, 0], // translateY
z: [0, 100], // translateZ (3D)
scale: [0.8, 1], // uniform scale
scaleX, scaleY, // axis scale
rotate: [0, 360], // 2D rotation (degrees)
rotateX, rotateY, rotateZ, // 3D rotation (degrees)
skew, skewX, skewY, // skew (degrees)
transform: Transform3D[], // 3D transform keyframes (see Transform3D section)
// Visual (all accept AnimatedValue)
opacity: [0, 1],
blur: [10, 0], // Gaussian blur in pixels
borderRadius: [0, 20],
color: ["#ff0000", "#00ff00"], // CSS color interpolation
backgroundColor: ["#000", "#fff"],
}}组件支持属性,格式如下:
transitiontsx
transition={{
// 时间设置
duration: 30, // 动画时长(帧)
delay: 10, // 延迟开始时间(帧)
frames: [0, 60], // 或显式设置帧范围(替代duration)
easing: "easeOutCubic", // 缓动曲线
// 变换属性(均支持AnimatedValue)
x: [30, 0], // X轴位移
y: [30, 0], // Y轴位移
z: [0, 100], // Z轴位移(3D)
scale: [0.8, 1], // 均匀缩放
scaleX, scaleY, // 单轴缩放
rotate: [0, 360], // 2D旋转(角度)
rotateX, rotateY, rotateZ, // 3D旋转(角度)
skew, skewX, skewY, // 倾斜(角度)
transform: Transform3D[], // 3D变换关键帧(参考Transform3D章节)
// 视觉属性(均支持AnimatedValue)
opacity: [0, 1],
blur: [10, 0], // 高斯模糊(像素)
borderRadius: [0, 20],
color: ["#ff0000", "#00ff00"], // CSS颜色插值
backgroundColor: ["#000", "#fff"],
}}4. Easing Functions
4. 缓动函数
tsx
type EasingName =
| "linear"
| "easeIn" | "easeOut" | "easeInOut"
| "easeInQuad" | "easeOutQuad" | "easeInOutQuad"
| "easeInCubic" | "easeOutCubic" | "easeInOutCubic"
| "easeInSine" | "easeOutSine" | "easeInOutSine"
| "easeInQuart" | "easeOutQuart" | "easeInOutQuart";Most common: for entries, for camera moves.
"easeOutCubic""easeInOutCubic"tsx
type EasingName =
| "linear"
| "easeIn" | "easeOut" | "easeInOut"
| "easeInQuad" | "easeOutQuad" | "easeInOutQuad"
| "easeInCubic" | "easeOutCubic" | "easeInOutCubic"
| "easeInSine" | "easeOutSine" | "easeInOutSine"
| "easeInQuart" | "easeOutQuart" | "easeInOutQuart";最常用的:元素入场使用,镜头移动使用。
"easeOutCubic""easeInOutCubic"5. Layout & Display Configuration
5. 布局与显示配置
CRITICAL: Bits are pre-wrapped with a layout container when displayed in the docs. The wrapper () provides an with default styling. When building standalone compositions, you must provide this layout yourself.
withShowcaseFillAbsoluteFill重要提示: 文档中展示的组件会自动包裹一层布局容器(),该容器提供了和默认样式。在构建独立合成内容时,你必须自行提供布局容器。
withShowcaseFillAbsoluteFillThe Layout Wrapper
布局容器说明
Bits displayed in docs are automatically wrapped with:
tsx
<AbsoluteFill style={{
backgroundColor: 'var(--color-background-dark)', // #100f0f
fontSize: `${config.width * 0.05}px`, // 5% of width
fontWeight: 700,
color: 'var(--color-primary-hover)', // #fcc192
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<Component />
</AbsoluteFill>This means simple bits like work because the wrapper centers them and provides background/color.
<AnimatedText>Hello</AnimatedText>文档中展示的组件会自动被以下容器包裹:
tsx
<AbsoluteFill style={{
backgroundColor: 'var(--color-background-dark)', // #100f0f
fontSize: `${config.width * 0.05}px`, // 宽度的5%
fontWeight: 700,
color: 'var(--color-primary-hover)', // #fcc192
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<Component />
</AbsoluteFill>这意味着像这样简单的组件能直接工作,因为容器已经处理了居中、背景和颜色。
<AnimatedText>Hello</AnimatedText>Building Standalone Compositions
构建独立合成内容
When creating compositions for direct use (not displayed through the docs wrapper), you MUST provide your own layout:
tsx
import { AbsoluteFill } from 'remotion';
export const MyComposition: React.FC = () => {
const rect = useViewportRect();
const { vmin } = rect;
return (
<AbsoluteFill style={{
backgroundColor: '#100f0f',
color: '#fcc192',
fontFamily: 'system-ui, sans-serif',
}}>
{/* Your content here */}
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
{/* Steps... */}
</Scene3D>
</AbsoluteFill>
);
};当创建直接使用的合成内容(而非通过文档容器展示)时,必须自行提供布局容器:
tsx
import { AbsoluteFill } from 'remotion';
export const MyComposition: React.FC = () => {
const rect = useViewportRect();
const { vmin } = rect;
return (
<AbsoluteFill style={{
backgroundColor: '#100f0f',
color: '#fcc192',
fontFamily: 'system-ui, sans-serif',
}}>
{/* 你的内容 */}
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
{/* 步骤... */}
</Scene3D>
</AbsoluteFill>
);
};CSS Variables (Theming)
CSS变量(主题)
Bits use CSS variables for consistent theming. These are available when rendering within the docs but must be defined or replaced with literal values in standalone projects:
| Variable | Default Value | Usage |
|---|---|---|
| | Accent orange |
| | Light orange / text color |
| | Dark background |
| | Surface/card background |
| | Lighter surface |
| | Dark borders |
| | Light borders |
In standalone projects: Replace with literal hex values, or define these variables in your HTML/CSS.
var(--color-*)组件使用CSS变量实现一致的主题。在文档中渲染时这些变量会自动生效,但在独立项目中必须定义这些变量或替换为具体值:
| 变量 | 默认值 | 用途 |
|---|---|---|
| | 主色调橙色 |
| | 浅橙色/文本颜色 |
| | 深色背景 |
| | 卡片/表面背景 |
| | 浅色表面 |
| | 深色边框 |
| | 浅色边框 |
在独立项目中: 将替换为具体的十六进制颜色值,或在HTML/CSS中定义这些变量。
var(--color-*)Common Layout Mistakes
常见布局错误
- Missing background: Content renders on transparent/white background. Always set on the outermost container.
backgroundColor - Missing AbsoluteFill: Scene3D and full-viewport compositions need from remotion as their root.
<AbsoluteFill> - Using CSS vars without defining them: resolves to nothing in a bare Remotion project. Use literal colors.
var(--color-primary) - Hardcoded pixel sizes: Use -based sizing from
vmininstead.useViewportRect() - Missing font settings: Set ,
fontSize,fontWeight, andfontFamilyexplicitly — there are no inherited defaults in Remotion.color
- 缺少背景: 内容在透明/白色背景上渲染。始终为最外层容器设置。
backgroundColor - 缺少AbsoluteFill: Scene3D和全屏合成内容需要使用Remotion提供的作为根容器。
<AbsoluteFill> - 未定义CSS变量: 在纯Remotion项目中,不会生效。使用具体颜色值。
var(--color-primary) - 硬编码像素尺寸: 使用提供的
useViewportRect()相关尺寸替代。vmin - 缺少字体设置: 显式设置、
fontSize、fontWeight和fontFamily——Remotion没有继承默认样式。color
Three Layout Patterns
三种布局模式
Simple (centered content, uses wrapper defaults):
tsx
// Relies on the outer wrapper for background, centering, font
export const Component = () => (
<AnimatedText transition={{ opacity: [0, 1] }}>Hello</AnimatedText>
);Self-contained (own background and layout):
tsx
export const Component = () => (
<div style={{
width: '100%', height: '100%',
backgroundColor: '#09090b',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<StaggeredMotion transition={{ scale: [0, 1], opacity: [0, 1] }}>
{items}
</StaggeredMotion>
</div>
);Full 3D scene (recommended for complex work):
tsx
export const Component = () => {
const rect = useViewportRect();
return (
<AbsoluteFill style={{
background: '#100f0f',
color: '#fcc192',
}}>
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
<Step id="intro" {...positions.base.toProps()} />
{/* ... */}
</Scene3D>
</AbsoluteFill>
);
};简单模式(居中内容,依赖容器默认样式):
tsx
// 依赖外层容器提供背景、居中、字体样式
export const Component = () => (
<AnimatedText transition={{ opacity: [0, 1] }}>Hello</AnimatedText>
);自包含模式(自带背景和布局):
tsx
export const Component = () => (
<div style={{
width: '100%', height: '100%',
backgroundColor: '#09090b',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<StaggeredMotion transition={{ scale: [0, 1], opacity: [0, 1] }}>
{items}
</StaggeredMotion>
</div>
);完整3D场景模式(复杂项目推荐):
tsx
export const Component = () => {
const rect = useViewportRect();
return (
<AbsoluteFill style={{
background: '#100f0f',
color: '#fcc192',
}}>
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
<Step id="intro" {...positions.base.toProps()} />
{/* ... */}
</Scene3D>
</AbsoluteFill>
);
};Transform3D (Critical for Complex Scenes)
Transform3D(复杂场景必备)
Transform3Dtsx
import { Transform3D, Vector3 } from "remotion-bits";Transform3Dtsx
import { Transform3D, Vector3 } from "remotion-bits";Creating Transforms
创建变换对象
tsx
const base = Transform3D.identity(); // Origin: position(0,0,0), rotation(0,0,0), scale(1,1,1)tsx
const base = Transform3D.identity(); // 初始状态:位置(0,0,0),旋转(0,0,0),缩放(1,1,1)Chaining Transforms
链式调用变换
Every method returns a new Transform3D. Chain freely:
tsx
const cardPosition = base
.translate(vmin * 50, vmin * -20, 0) // Move right and up
.rotateY(-15) // Rotate around Y axis (degrees)
.scaleBy(1.5); // Scale uniformly by 1.5x每个方法都会返回新的Transform3D对象,可以自由链式调用:
tsx
const cardPosition = base
.translate(vmin * 50, vmin * -20, 0) // 向右向上移动
.rotateY(-15) // 绕Y轴旋转(角度)
.scaleBy(1.5); // 均匀缩放1.5倍Available Methods
可用方法
tsx
// Position
transform.translate(x, y, z) // Add to position
transform.translate(vector3) // Add Vector3 to position
// Rotation (angles in DEGREES)
transform.rotateX(degrees) // Rotate around X axis
transform.rotateY(degrees) // Rotate around Y axis
transform.rotateZ(degrees) // Rotate around Z axis
transform.rotateAround(origin, axis, degrees) // Rotate around arbitrary point+axis
// Scale
transform.scaleBy(uniform) // Scale all axes equally
transform.scaleBy(sx, sy, sz) // Scale per-axis
// Composition
transform.multiply(other) // Matrix multiplication
transform.inverse() // Invert transform
transform.lerp(target, alpha) // Linear interpolation (0-1)
transform.clone() // Deep copy
// Randomization (deterministic via seed)
transform.randomTranslate([minX, maxX], [minY, maxY], [minZ, maxZ], seed)
transform.randomRotateX([minDeg, maxDeg], seed)
transform.randomRotateY([minDeg, maxDeg], seed)
transform.randomRotateZ([minDeg, maxDeg], seed)
// Conversion
transform.toProps() // → { x, y, z, rotateX, rotateY, rotateZ, scaleX, scaleY, scaleZ, rotateOrder }
transform.toCSSMatrix3D() // → "matrix3d(...)" CSS string
transform.toMatrix4() // → Three.js Matrix4tsx
// 位置
transform.translate(x, y, z) // 添加位置偏移
transform.translate(vector3) // 添加Vector3位置偏移
// 旋转(角度为度数)
transform.rotateX(degrees) // 绕X轴旋转
transform.rotateY(degrees) // 绕Y轴旋转
transform.rotateZ(degrees) // 绕Z轴旋转
transform.rotateAround(origin, axis, degrees) // 绕任意点和轴旋转
// 缩放
transform.scaleBy(uniform) // 所有轴均匀缩放
transform.scaleBy(sx, sy, sz) // 单轴独立缩放
// 变换组合
transform.multiply(other) // 矩阵乘法
transform.inverse() // 反转变换
transform.lerp(target, alpha) // 线性插值(0-1)
transform.clone() // 深拷贝
// 随机化(通过种子保证确定性)
transform.randomTranslate([minX, maxX], [minY, maxY], [minZ, maxZ], seed)
transform.randomRotateX([minDeg, maxDeg], seed)
transform.randomRotateY([minDeg, maxDeg], seed)
transform.randomRotateZ([minDeg, maxDeg], seed)
// 转换
transform.toProps() // → { x, y, z, rotateX, rotateY, rotateZ, scaleX, scaleY, scaleZ, rotateOrder }
transform.toCSSMatrix3D() // → "matrix3d(...)" CSS字符串
transform.toMatrix4() // → Three.js Matrix4对象Using Transform3D with Step/Element3D
结合Step/Element3D使用Transform3D
The method converts a Transform3D to props that Step and Element3D accept:
.toProps()tsx
const position = base.translate(vmin * 50, 0, 0).rotateY(-15);
// Spread directly into Step or Element3D
<Step id="my-step" {...position.toProps()} />This is equivalent to manually specifying .
x={...} y={...} z={...} rotateX={...} rotateY={...} rotateZ={...}.toProps()tsx
const position = base.translate(vmin * 50, 0, 0).rotateY(-15);
// 直接展开到Step或Element3D中
<Step id="my-step" {...position.toProps()} />这等同于手动设置。
x={...} y={...} z={...} rotateX={...} rotateY={...} rotateZ={...}Using Transform3D as Keyframes
将Transform3D作为关键帧
Pass arrays as the property for smooth 3D interpolation between positions:
Transform3D[]transformtsx
const start = base.translate(0, vmin * 20, 0);
const end = base.translate(0, 0, 0);
<Element3D
centered
transition={{
duration: 35,
transform: [start, end], // Interpolates position, rotation, scale via slerp
opacity: [0, 1],
easing: "easeInOutCubic",
}}
>
<div>Animates from start to end</div>
</Element3D>Transform keyframes use quaternion slerp for rotation (no gimbal lock) and linear interpolation for position and scale.
传递数组作为属性,可实现位置之间的平滑3D插值:
Transform3D[]transformtsx
const start = base.translate(0, vmin * 20, 0);
const end = base.translate(0, 0, 0);
<Element3D
centered
transition={{
duration: 35,
transform: [start, end], // 通过球面线性插值(slerp)实现位置、旋转、缩放的平滑过渡
opacity: [0, 1],
easing: "easeInOutCubic",
}}
>
<div>从起始位置动画到结束位置</div>
</Element3D>变换关键帧使用四元数球面线性插值(slerp)处理旋转(避免万向锁问题),使用线性插值处理位置和缩放。
Vector3
Vector3
Three.js is re-exported for position math:
Vector3tsx
const offset = new Vector3(0, -vmin * 2, 0);
const farOffset = offset.clone().multiplyScalar(4.0); // Scale the offset
const combined = offset.clone().add(new Vector3(vmin * 5, 0, 0));重新导出了Three.js的用于位置计算:
Vector3tsx
const offset = new Vector3(0, -vmin * 2, 0);
const farOffset = offset.clone().multiplyScalar(4.0); // 缩放偏移量
const combined = offset.clone().add(new Vector3(vmin * 5, 0, 0));Scene3D System (3D Presentations)
Scene3D系统(3D展示)
Scene3D creates camera-based 3D presentations (like impress.js). The camera flies between Steps; content is placed in 3D space.
Scene3D用于创建基于相机的3D展示内容(类似impress.js)。相机会在不同Step之间飞行,内容放置在3D空间中。
Architecture
架构
Scene3D (perspective, timing)
├── Step (camera target 1) — children visible during this step
├── Step (camera target 2) — children visible during this step
├── ...
├── StepResponsive — element that animates differently per step
├── StepResponsive — another step-aware element
└── (any other children — always rendered)Scene3D(透视、时间设置)
├── Step(相机目标1)—— 该步骤激活时显示子内容
├── Step(相机目标2)—— 该步骤激活时显示子内容
├── ...
├── StepResponsive —— 随步骤变化的动画元素
├── StepResponsive —— 另一个步骤感知元素
└──(其他子元素——始终显示)Scene3D Container
Scene3D容器
tsx
<Scene3D
perspective={1000} // CSS perspective in px (depth effect)
stepDuration={60} // Default frames per step
transitionDuration={60} // Frames for camera transitions between steps
easing="easeInOutCubic" // Camera transition easing
>
{/* Steps and content */}
</Scene3D>tsx
<Scene3D
perspective={1000} // CSS透视值(像素,控制景深效果)
stepDuration={60} // 每个步骤的默认时长(帧)
transitionDuration={60} // 步骤间相机过渡的时长(帧)
easing="easeInOutCubic" // 相机过渡的缓动曲线
>
{/* 步骤和内容 */}
</Scene3D>Steps (Camera Targets)
Step(相机目标)
Steps define where the camera flies to. They execute sequentially. Content inside a Step is visible when that step is active.
tsx
<Step
id="intro" // Unique identifier (used by StepResponsive)
{...position.toProps()} // Camera target position/rotation/scale
duration={120} // Override stepDuration for this step (optional)
transition={{ // Animate children on step ENTRY (optional)
opacity: [0, 1],
blur: [10, 0],
duration: 20,
}}
exitTransition={{ // Animate children on step EXIT (optional)
opacity: [1, 0],
blur: [0, 10],
}}
>
{/* Content shown during this step */}
<FloatingCard>...</FloatingCard>
</Step>Step timing model:
- Steps play sequentially: step1 → transition → step2 → transition → step3
- (on Scene3D): default duration each step is active
stepDuration - (on Step): override for specific step
duration - (on Scene3D): how long the camera takes to move between steps
transitionDuration - Total composition should be ≥ sum of all step durations
Step定义了相机要飞往的位置。它们按顺序执行。Step内部的内容仅在该步骤激活时可见。
tsx
<Step
id="intro" // 唯一标识符(供StepResponsive使用)
{...position.toProps()} // 相机目标的位置/旋转/缩放属性
duration={120} // 覆盖该步骤的默认时长(可选)
transition={{ // 步骤激活时子元素的入场动画(可选)
opacity: [0, 1],
blur: [10, 0],
duration: 20,
}}
exitTransition={{ // 步骤结束时子元素的退场动画(可选)
opacity: [1, 0],
blur: [0, 10],
}}
>
{/* 该步骤显示的内容 */}
<FloatingCard>...</FloatingCard>
</Step>Step时间模型:
- Step按顺序执行:step1 → 过渡 → step2 → 过渡 → step3
- (Scene3D上的属性):每个步骤的默认激活时长
stepDuration - (Step上的属性):覆盖特定步骤的时长
duration - (Scene3D上的属性):相机在步骤间移动的时长
transitionDuration - 合成内容总时长应≥所有步骤时长之和
Element3D (3D Positioned Content)
Element3D(3D定位内容)
Places content at a specific 3D position, independent of camera:
tsx
<Element3D
centered // Center-align the element (transform-origin: center)
x={vmin * 50} y={0} z={0} // Position in 3D space
style={{ width: vmin * 60 }}
transition={{ // Animate on mount (optional)
delay: 20,
opacity: [0, 1],
duration: 35,
transform: [startTransform, endTransform], // 3D keyframes
easing: "easeInOutCubic",
}}
>
<div>Content positioned in 3D space</div>
</Element3D>将内容放置在3D空间的特定位置,与相机无关:
tsx
<Element3D
centered // 元素居中对齐(transform-origin: center)
x={vmin * 50} y={0} z={0} // 3D空间位置
style={{ width: vmin * 60 }}
transition={{ // 挂载时的动画(可选)
delay: 20,
opacity: [0, 1],
duration: 35,
transform: [startTransform, endTransform], // 3D关键帧
easing: "easeInOutCubic",
}}
>
<div>放置在3D空间中的内容</div>
</Element3D>StepResponsive (Step-Aware Animations)
StepResponsive(步骤感知动画)
The key to complex scenes. Elements define how they should look/position at each step, and animate between states when the camera moves:
tsx
<StepResponsive
centered // Center the child
style={{ position: 'absolute', fontSize }}
steps={{
// Key = step ID, Value = properties at that step
'intro': {
transform: [base, shiftedPosition], // Transform3D keyframes
opacity: [0, 1],
},
'elements': {
transform: [elementPosition], // Hold at this position
},
'outro': {
transform: [outroStart, outroEnd],
opacity: [1, 1, 1, 0], // Hold visible, then fade
duration: "step", // Match step duration
easing: "easeInOutCubic",
},
}}
>
<h1>Title That Follows Camera</h1>
</StepResponsive>StepResponsive key behaviors:
- Properties accumulate/inherit: if step "elements" doesn't set opacity, it keeps the value from the last step that set it
- Arrays flatten to their final value when moving to the next step (no re-animation of past keyframes)
- accepts
transformarrays — the primary way to position elements in 3DTransform3D[] - makes the animation last the entire step duration
duration: "step" - You can map the same props to multiple step IDs to hold position across steps
Mapping same props to multiple steps (common pattern):
tsx
const mapToAllElementSteps = (props) => ({
'elements': props,
'element-particles': props,
'element-text': props,
'element-code': props,
});
<StepResponsive
steps={{
'intro': { transform: [introPosition] },
...mapToAllElementSteps({ transform: [elementPosition] }),
'outro': { transform: [outroPosition] },
}}
>
<h1>Elements</h1>
</StepResponsive>这是构建复杂场景的关键。元素可以定义在每个步骤中的样式/位置,相机动画时元素会自动在不同状态间过渡:
tsx
<StepResponsive
centered // 子元素居中
style={{ position: 'absolute', fontSize }}
steps={{
// 键=步骤ID,值=该步骤的属性
'intro': {
transform: [base, shiftedPosition], // Transform3D关键帧
opacity: [0, 1],
},
'elements': {
transform: [elementPosition], // 保持该位置
},
'outro': {
transform: [outroStart, outroEnd],
opacity: [1, 1, 1, 0], // 保持可见,然后淡出
duration: "step", // 动画时长等于步骤时长
easing: "easeInOutCubic",
},
}}
>
<h1>跟随相机的标题</h1>
</StepResponsive>StepResponsive核心特性:
- 属性会累积/继承:如果步骤"elements"没有设置opacity,会沿用最后一个设置过opacity的步骤的值
- 数组在切换到下一个步骤时会取最终值(不会重新播放之前的关键帧动画)
- 接受
transform数组——这是3D空间中定位元素的主要方式Transform3D[] - 让动画时长等于整个步骤的时长
duration: "step" - 可以将相同属性映射到多个步骤ID,实现跨步骤保持位置
将相同属性映射到多个步骤(常见模式):
tsx
const mapToAllElementSteps = (props) => ({
'elements': props,
'element-particles': props,
'element-text': props,
'element-code': props,
});
<StepResponsive
steps={{
'intro': { transform: [introPosition] },
...mapToAllElementSteps({ transform: [elementPosition] }),
'outro': { transform: [outroPosition] },
}}
>
<h1>元素</h1>
</StepResponsive>Building Complex Scenes (Architecture Guide)
构建复杂场景(架构指南)
This is the recommended approach for any non-trivial composition. Scene3D provides camera management, step-based timing, 3D element positioning, and step-responsive animations — eliminating the need to manually manage , , or CSS transforms.\n\n### Step 1: Plan the Scene Structure
useCurrentFrame()<Sequence>Decide on the major sections (acts) and what the camera shows in each:
intro → elements → element-particles → element-text → ... → transitions → scenes → outroEach section = one Step. Steps execute sequentially.
这是构建非简单合成内容的推荐方法。 Scene3D提供了相机管理、基于步骤的时间控制、3D元素定位和步骤感知动画——无需手动管理、或CSS变换。
useCurrentFrame()<Sequence>Step 2: Pre-compute All Positions
步骤1:规划场景结构
Use to build a position tree. This is THE critical architectural pattern:
useMemotsx
const positions = useMemo(() => {
const { vmin } = rect;
const base = Transform3D.identity();
// Define base positions for each scene section
const elementsBase = base.translate(0, -vmin * 120, 0).rotateX(15);
const transitionsBase = base.translate(vmin * 200, vmin * 50, 0).rotateY(-15);
const scenesBase = base.translate(-vmin * 120, vmin * 70, 0).rotateY(15);
// Derive sub-positions from bases
const cardW = vmin * 70;
const particlesCard = elementsBase.translate(-cardW, -vmin * 40, 0).rotateY(15);
const textCard = elementsBase.translate(0, -vmin * 50, vmin * 10).rotateX(10);
return {
base,
elements: {
base: elementsBase,
cards: { particles: particlesCard, text: textCard },
},
transitions: { base: transitionsBase },
scenes: { base: scenesBase },
};
}, [rect.width, rect.height]); // Re-compute on resizeWhy pre-compute positions?
- Separation of layout from rendering
- Positions can be derived from each other (hierarchy)
- Easy to adjust entire sections by changing the base
- StepResponsive needs position references, not inline calculations
确定主要章节(段落)以及每个章节中相机要展示的内容:
intro → elements → element-particles → element-text → ... → transitions → scenes → outro每个章节对应一个Step,Step按顺序执行。
Step 3: Define Steps
步骤2:预计算所有位置
tsx
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
<Step id="intro" {...positions.base.toProps()} />
<Step id="elements" {...positions.elements.base.toProps()} />
<Step id="element-particles"
{...positions.elements.cards.particles.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}
>
<FloatingCard><ParticleDemo /></FloatingCard>
</Step>
<Step id="transitions" duration={120} {...positions.transitions.base.toProps()} />
<Step id="scenes" duration={120} {...positions.scenes.base.toProps()} />
<Step id="outro" {...positions.base.toProps()} duration={120} />
</Scene3D>使用构建位置树。这是至关重要的架构模式:
useMemotsx
const positions = useMemo(() => {
const { vmin } = rect;
const base = Transform3D.identity();
// 定义每个场景章节的基础位置
const elementsBase = base.translate(0, -vmin * 120, 0).rotateX(15);
const transitionsBase = base.translate(vmin * 200, vmin * 50, 0).rotateY(-15);
const scenesBase = base.translate(-vmin * 120, vmin * 70, 0).rotateY(15);
// 从基础位置派生子位置
const cardW = vmin * 70;
const particlesCard = elementsBase.translate(-cardW, -vmin * 40, 0).rotateY(15);
const textCard = elementsBase.translate(0, -vmin * 50, vmin * 10).rotateX(10);
return {
base,
elements: {
base: elementsBase,
cards: { particles: particlesCard, text: textCard },
},
transitions: { base: transitionsBase },
scenes: { base: scenesBase },
};
}, [rect.width, rect.height]); // 尺寸变化时重新计算为什么要预计算位置?
- 布局与渲染分离
- 位置可以通过层级关系派生
- 只需修改基础位置即可调整整个章节的布局
- StepResponsive需要位置引用,而非内联计算
Step 4: Add Step-Responsive Elements
步骤3:定义Step
Titles, icons, and persistent elements that move with the camera:
tsx
<StepResponsive
centered
style={{ fontSize: vmin * 10, position: 'absolute' }}
steps={{
'intro': { transform: [positions.base, positions.base.translate(vmin * 7, 0, 0)] },
'elements': { transform: [positions.elements.base.translate(vmin * 7, 0, 0)] },
'outro': {
transform: [positions.base.translate(vmin * 7, 0, 0), positions.base],
duration: "step",
easing: "easeInOutCubic",
},
}}
>
<h1>Remotion Bits</h1>
</StepResponsive>tsx
<Scene3D perspective={1000} stepDuration={60} transitionDuration={60}>
<Step id="intro" {...positions.base.toProps()} />
<Step id="elements" {...positions.elements.base.toProps()} />
<Step id="element-particles"
{...positions.elements.cards.particles.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}
>
<FloatingCard><ParticleDemo /></FloatingCard>
</Step>
<Step id="transitions" duration={120} {...positions.transitions.base.toProps()} />
<Step id="scenes" duration={120} {...positions.scenes.base.toProps()} />
<Step id="outro" {...positions.base.toProps()} duration={120} />
</Scene3D>Step 5: Content Inside Steps
步骤4:添加Step-Responsive元素
Steps can contain rich content — cards, particles, code blocks, counters:
tsx
<Step id="element-particles" {...cardPos.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}>
<FloatingCard>
<Particles style={{ position: 'absolute', inset: 0, opacity: 0.6 }}>
<Spawner rate={1} max={200} lifespan={80}
velocity={{ x: 0, y: -0.6, varianceX: 0.4, varianceY: 0.2 }}
area={{ width: rect.width, height: rect.height }}>
<div style={{ width: vmin * 2, height: vmin * 2, borderRadius: '50%',
background: 'var(--color-primary)' }} />
</Spawner>
<Behavior drag={0.96}
wiggle={{ magnitude: 0.6, frequency: 0.25 }}
opacity={[1, 0]}
scale={{ start: 1, end: 0.4 }} />
</Particles>
<span style={{ position: 'relative', zIndex: 1, fontWeight: 'bold',
fontSize: vmin * 3, fontFamily: 'monospace' }}>Particles</span>
</FloatingCard>
</Step>添加标题、图标和其他随相机移动的持久元素:
tsx
<StepResponsive
centered
style={{ fontSize: vmin * 10, position: 'absolute' }}
steps={{
'intro': { transform: [positions.base, positions.base.translate(vmin * 7, 0, 0)] },
'elements': { transform: [positions.elements.base.translate(vmin * 7, 0, 0)] },
'outro': {
transform: [positions.base.translate(vmin * 7, 0, 0), positions.base],
duration: "step",
easing: "easeInOutCubic",
},
}}
>
<h1>Remotion Bits</h1>
</StepResponsive>Components Quick Reference
步骤5:Step内部的内容
AnimatedText
—
tsx
<AnimatedText
style={{ fontSize: vmin * 5, fontWeight: 'bold' }}
transition={{
split: "character", // "none" | "word" | "character" | "line" | custom separator
splitStagger: 2, // Frames between each unit
opacity: [0, 1],
y: [15, 0],
blur: [2, 0],
duration: 20,
delay: 10,
easing: "easeOutCubic",
// Cycling text:
cycle: { texts: ["Build", "Create", "Ship"], itemDuration: 40 },
// Glitch effect:
glitch: [0.6, 0], // Glitch intensity AnimatedValue
}}
>
Hello World
</AnimatedText>Step可以包含丰富的内容——卡片、粒子、代码块、计数器等:
tsx
<Step id="element-particles" {...cardPos.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}>
<FloatingCard>
<Particles style={{ position: 'absolute', inset: 0, opacity: 0.6 }}>
<Spawner rate={1} max={200} lifespan={80}
velocity={{ x: 0, y: -0.6, varianceX: 0.4, varianceY: 0.2 }}
area={{ width: rect.width, height: rect.height }}>
<div style={{ width: vmin * 2, height: vmin * 2, borderRadius: '50%',
background: 'var(--color-primary)' }} />
</Spawner>
<Behavior drag={0.96}
wiggle={{ magnitude: 0.6, frequency: 0.25 }}
opacity={[1, 0]}
scale={{ start: 1, end: 0.4 }} />
</Particles>
<span style={{ position: 'relative', zIndex: 1, fontWeight: 'bold',
fontSize: vmin * 3, fontFamily: 'monospace' }}>Particles</span>
</FloatingCard>
</Step>AnimatedCounter
组件快速参考
—
AnimatedText
tsx
<AnimatedCounter
transition={{
values: [0, 1000], // Number keyframes
color: ['#ff0000', '#00ff00'], // Color transition
scale: [0.8, 1],
duration: 30,
delay: 10,
}}
prefix="$"
postfix="+"
toFixed={0}
style={{ fontSize: vmin * 4, fontWeight: 'bold' }}
/>tsx
<AnimatedText
style={{ fontSize: vmin * 5, fontWeight: 'bold' }}
transition={{
split: "character", // "none" | "word" | "character" | "line" | 自定义分隔符
splitStagger: 2, // 每个单元的动画延迟(帧)
opacity: [0, 1],
y: [15, 0],
blur: [2, 0],
duration: 20,
delay: 10,
easing: "easeOutCubic",
// 循环文本:
cycle: { texts: ["Build", "Create", "Ship"], itemDuration: 40 },
// 故障效果:
glitch: [0.6, 0], // 故障强度AnimatedValue
}}
>
Hello World
</AnimatedText>TypeWriter
AnimatedCounter
tsx
<TypeWriter
text="import { TypeWriter } from 'remotion-bits';"
typeSpeed={2} // Frames per character
deleteSpeed={1}
pauseAfterType={60} // Frames to wait before deleting
delay={30} // Start delay
cursor="▋"
style={{ fontSize: vmin * 2, fontFamily: 'monospace', whiteSpace: 'pre' }}
/>
// Multiple texts with cycling:
<TypeWriter
text={["First line", "Second line", "Third line"]}
deleteBeforeNext={true}
loop={false}
errorRate={0.05} // 5% chance of typo
/>tsx
<AnimatedCounter
transition={{
values: [0, 1000], // 数值关键帧
color: ['#ff0000', '#00ff00'], // 颜色过渡
scale: [0.8, 1],
duration: 30,
delay: 10,
}}
prefix="$"
postfix="+"
toFixed={0}
style={{ fontSize: vmin * 4, fontWeight: 'bold' }}
/>CodeBlock
TypeWriter
tsx
<CodeBlock
code={`const x = useViewportRect();\nconsole.log(x.vmin);`}
language="tsx"
theme="dark"
showLineNumbers={false}
fontSize={vmin * 1.2}
padding={vmin * 1.5}
transition={{
duration: 20,
delay: 10,
lineStagger: 2, // Frames between lines
lineStaggerDirection: "forward", // "forward" | "reverse" | "center" | "random"
opacity: [0, 1],
y: [8, 0],
blur: [10, 0],
}}
highlight={[{ lines: [2, 3], color: "rgba(59,130,246,0.15)", opacity: [0, 1] }]}
focus={{ lines: [2, 3], dimOpacity: 0.3, dimBlur: 2 }}
/>tsx
<TypeWriter
text="import { TypeWriter } from 'remotion-bits';"
typeSpeed={2} // 每个字符的输入时长(帧)
deleteSpeed={1}
pauseAfterType={60} // 输入完成后的暂停时长(帧)
delay={30} // 开始延迟(帧)
cursor="▋"
style={{ fontSize: vmin * 2, fontFamily: 'monospace', whiteSpace: 'pre' }}
/>
// 多文本循环:
<TypeWriter
text={["First line", "Second line", "Third line"]}
deleteBeforeNext={true}
loop={false}
errorRate={0.05} // 5%的输入错误概率
/>StaggeredMotion
CodeBlock
tsx
<StaggeredMotion
transition={{
opacity: [0, 1],
scale: [0, 1],
y: [20, 0],
duration: 30,
delay: 5,
stagger: 3, // Frames between each child
staggerDirection: "forward", // "forward"|"reverse"|"center"|"random"
easing: "easeOutCubic",
borderRadius: 4,
}}
>
<div>Child 1</div>
<div>Child 2</div>
<div>Child 3</div>
</StaggeredMotion>tsx
<CodeBlock
code={`const x = useViewportRect();\nconsole.log(x.vmin);`}
language="tsx"
theme="dark"
showLineNumbers={false}
fontSize={vmin * 1.2}
padding={vmin * 1.5}
transition={{
duration: 20,
delay: 10,
lineStagger: 2, // 每行的动画延迟(帧)
lineStaggerDirection: "forward", // "forward" | "reverse" | "center" | "random"
opacity: [0, 1],
y: [8, 0],
blur: [10, 0],
}}
highlight={[{ lines: [2, 3], color: "rgba(59,130,246,0.15)", opacity: [0, 1] }]}
focus={{ lines: [2, 3], dimOpacity: 0.3, dimBlur: 2 }}
/>GradientTransition
StaggeredMotion
tsx
<GradientTransition
gradient={[
"linear-gradient(0deg, #051226, #1e0541)",
"linear-gradient(180deg, #a5d4dd, #5674b1)",
]}
duration={90}
easing="easeInOut"
shortestAngle={true} // Interpolate angles via shortest path
/>tsx
<StaggeredMotion
transition={{
opacity: [0, 1],
scale: [0, 1],
y: [20, 0],
duration: 30,
delay: 5,
stagger: 3, // 每个子元素的动画延迟(帧)
staggerDirection: "forward", // "forward"|"reverse"|"center"|"random"
easing: "easeOutCubic",
borderRadius: 4,
}}
>
<div>Child 1</div>
<div>Child 2</div>
<div>Child 3</div>
</StaggeredMotion>MatrixRain
GradientTransition
tsx
<MatrixRain fontSize={18} color="#00FF00" speed={1.2} density={0.8} streamLength={20} />tsx
<GradientTransition
gradient={[
"linear-gradient(0deg, #051226, #1e0541)",
"linear-gradient(180deg, #a5d4dd, #5674b1)",
]}
duration={90}
easing="easeInOut"
shortestAngle={true} // 通过最短路径插值角度
/>ScrollingColumns
MatrixRain
tsx
<ScrollingColumns
columns={[
{ images: ["/img1.jpg", "/img2.jpg"], speed: 100, direction: "up" },
{ images: ["/img3.jpg", "/img4.jpg"], speed: 150, direction: "down" },
]}
height={rect.vmin * 40}
gap={rect.vmin * 2}
/>tsx
<MatrixRain fontSize={18} color="#00FF00" speed={1.2} density={0.8} streamLength={20} />Particle System
ScrollingColumns
tsx
<Particles style={{ position: 'absolute', inset: 0 }}>
<Spawner
rate={2} // Particles per frame
max={200} // Maximum alive particles
lifespan={100} // Frames each particle lives
burst={50} // Emit 50 on first frame (optional)
position={resolvePoint(rect, "center")}
area={{ width: rect.width, height: 0 }} // Spawn area
velocity={{ x: 0, y: -2, varianceX: 1, varianceY: 0.5 }}
>
{/* Multiple children = random selection per particle */}
<div style={{ width: vmin * 2, height: vmin * 2, borderRadius: '50%', background: '#fff' }} />
<div style={{ width: vmin * 1, height: vmin * 1, borderRadius: '50%', background: '#aaa' }} />
</Spawner>
<Behavior
gravity={{ y: 0.1 }} // Constant force
drag={0.96} // Air resistance (1=none, 0=instant stop)
wiggle={{ magnitude: 0.6, frequency: 0.25 }} // Random oscillation
opacity={[1, 0]} // Fade out over lifetime
scale={{ start: 1, end: 0.3, startVariance: 0.2 }} // Shrink over lifetime
/>
</Particles>tsx
<ScrollingColumns
columns={[
{ images: ["/img1.jpg", "/img2.jpg"], speed: 100, direction: "up" },
{ images: ["/img3.jpg", "/img4.jpg"], speed: 150, direction: "down" },
]}
height={rect.vmin * 40}
gap={rect.vmin * 2}
/>Procedural Content Generation
粒子系统
For grids, backgrounds, and data-driven visuals, use deterministic :
random()tsx
import { random } from 'remotion-bits';
const items = useMemo(() => {
const palette = ['#fb4934', '#b8bb26', '#fabd2f', '#83a598'];
const result = [];
for (let r = 0; r < 10; r++) {
for (let c = 0; c < 20; c++) {
const seed = `grid-${r}-${c}`;
const colorIndex = Math.floor(random(seed + 'color') * palette.length);
const dist = Math.sqrt(r * r + c * c);
result.push(
<StaggeredMotion key={`${r}-${c}`}
transition={{
opacity: [0, 1],
scale: [0, 1],
delay: 30 + dist * 2, // Wave from top-left
duration: 40,
}}>
<div style={{
width: vmin * 5, height: vmin * 5,
background: palette[colorIndex],
}} />
</StaggeredMotion>
);
}
}
return result;
}, [vmin]);random(seed)tsx
<Particles style={{ position: 'absolute', inset: 0 }}>
<Spawner
rate={2} // 每帧生成的粒子数
max={200} // 最大存活粒子数
lifespan={100} // 每个粒子的生命周期(帧)
burst={50} // 第一帧生成50个粒子(可选)
position={resolvePoint(rect, "center")}
area={{ width: rect.width, height: 0 }} // 生成区域
velocity={{ x: 0, y: -2, varianceX: 1, varianceY: 0.5 }}
>
{/* 多个子元素——每个粒子随机选择一个 */}
<div style={{ width: vmin * 2, height: vmin * 2, borderRadius: '50%', background: '#fff' }} />
<div style={{ width: vmin * 1, height: vmin * 1, borderRadius: '50%', background: '#aaa' }} />
</Spawner>
<Behavior
gravity={{ y: 0.1 }} // 恒定重力
drag={0.96} // 空气阻力(1=无阻力,0=立即停止)
wiggle={{ magnitude: 0.6, frequency: 0.25 }} // 随机摆动
opacity={[1, 0]} // 生命周期内淡出
scale={{ start: 1, end: 0.3, startVariance: 0.2 }} // 生命周期内缩小
/>
</Particles>Utility Functions
程序化内容生成
interpolate (non-monotonic)
—
Unlike Remotion's built-in, supports non-monotonic input ranges:
tsx
import { interpolate, Easing } from "remotion-bits";
// Hold then animate
interpolate(frame, [0, 30, 30, 60], [0, 0, 100, 100]);
// With easing
interpolate(frame, [0, 60], [0, 100], { easing: "easeOutCubic" });
// Clamp extrapolation
interpolate(frame, [0, 60], [0, 100], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });对于网格、背景和数据驱动的视觉效果,使用确定性的函数:
random()tsx
import { random } from 'remotion-bits';
const items = useMemo(() => {
const palette = ['#fb4934', '#b8bb26', '#fabd2f', '#83a598'];
const result = [];
for (let r = 0; r < 10; r++) {
for (let c = 0; c < 20; c++) {
const seed = `grid-${r}-${c}`;
const colorIndex = Math.floor(random(seed + 'color') * palette.length);
const dist = Math.sqrt(r * r + c * c);
result.push(
<StaggeredMotion key={`${r}-${c}`}
transition={{
opacity: [0, 1],
scale: [0, 1],
delay: 30 + dist * 2, // 从左上角开始的波浪式动画
duration: 40,
}}>
<div style={{
width: vmin * 5, height: vmin * 5,
background: palette[colorIndex],
}} />
</StaggeredMotion>
);
}
}
return result;
}, [vmin]);random(seed)Color Interpolation
工具函数
—
interpolate(支持非单调)
tsx
import { interpolateColorKeyframes } from "remotion-bits";
// Perceptually uniform (Oklch) color transitions
const color = interpolateColorKeyframes(["#ff0000", "#00ff00", "#0000ff"], progress);与Remotion内置的interpolate不同,该函数支持非单调输入范围:
tsx
import { interpolate, Easing } from "remotion-bits";
// 先保持,再动画
interpolate(frame, [0, 30, 30, 60], [0, 0, 100, 100]);
// 带缓动曲线
interpolate(frame, [0, 60], [0, 100], { easing: "easeOutCubic" });
// 限制插值范围
interpolate(frame, [0, 60], [0, 100], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });Geometry
颜色插值
tsx
import { resolvePoint, createRect, Rect } from "remotion-bits";
const point = resolvePoint(rect, "center"); // { x: 960, y: 540 }
const point2 = resolvePoint(rect, { x: "50%", y: "25%" }); // { x: 960, y: 270 }tsx
import { interpolateColorKeyframes } from "remotion-bits";
// 感知均匀的(Oklch)颜色过渡
const color = interpolateColorKeyframes(["#ff0000", "#00ff00", "#0000ff"], progress);Bit Metadata Format
几何工具
Bits export a object and a :
metadataComponenttsx
export const metadata = {
name: "MyBit",
description: "Description of the bit.",
tags: ["tag1", "tag2"],
duration: 300, // Total frames
width: 1920,
height: 1080,
registry: {
name: "bit-my-bit",
title: "My Bit",
description: "Registry description.",
type: "bit" as const,
add: "when-needed" as const,
registryDependencies: ["animated-text", "scene-3d", "use-viewport-rect"],
dependencies: [],
files: [{ path: "path/to/MyBit.tsx" }],
},
};
export const Component: React.FC = () => { /* ... */ };tsx
import { resolvePoint, createRect, Rect } from "remotion-bits";
const point = resolvePoint(rect, "center"); // { x: 960, y: 540 }
const point2 = resolvePoint(rect, { x: "50%", y: "25%" }); // { x: 960, y: 270 }Common Patterns
Bit元数据格式
Floating Card Container
—
tsx
const FloatingCard = ({ children }) => (
<div style={{
position: 'relative',
width: vmin * 60, height: vmin * 30,
background: 'rgba(20, 20, 30, 0.6)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.2)',
borderRadius: vmin * 1,
display: 'flex', alignItems: 'center', justifyContent: 'center',
overflow: 'hidden',
boxShadow: '0 0 20px rgba(0,0,0,0.2)',
}}>
{children}
</div>
);Bits会导出对象和组件:
metadataComponenttsx
export const metadata = {
name: "MyBit",
description: "Bit的描述。",
tags: ["tag1", "tag2"],
duration: 300, // 总帧数
width: 1920,
height: 1080,
registry: {
name: "bit-my-bit",
title: "My Bit",
description: "注册表描述。",
type: "bit" as const,
add: "when-needed" as const,
registryDependencies: ["animated-text", "scene-3d", "use-viewport-rect"],
dependencies: [],
files: [{ path: "path/to/MyBit.tsx" }],
},
};
export const Component: React.FC = () => { /* ... */ };Scene with Entry Animations per Step
常见模式
—
悬浮卡片容器
tsx
<Step id="particles-demo" {...cardPosition.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}>
<FloatingCard>
{/* Content appears when camera arrives */}
</FloatingCard>
</Step>tsx
const FloatingCard = ({ children }) => (
<div style={{
position: 'relative',
width: vmin * 60, height: vmin * 30,
background: 'rgba(20, 20, 30, 0.6)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.2)',
borderRadius: vmin * 1,
display: 'flex', alignItems: 'center', justifyContent: 'center',
overflow: 'hidden',
boxShadow: '0 0 20px rgba(0,0,0,0.2)',
}}>
{children}
</div>
);Step-Responsive Title Tracking Camera
带步骤入场动画的场景
tsx
<StepResponsive
centered
style={{ fontSize: vmin * 10, position: 'absolute', width: 'max-content' }}
steps={{
'intro': { transform: [startPos] },
'main': { transform: [mainPos] },
'outro': { transform: [mainPos, endPos], duration: "step", easing: "easeInOutCubic" },
}}
>
<h1>Section Title</h1>
</StepResponsive>tsx
<Step id="particles-demo" {...cardPosition.toProps()}
transition={{ opacity: [0, 1], blur: [10, 0] }}>
<FloatingCard>
{/* 相机到达时内容显示 */}
</FloatingCard>
</Step>Animated Grid with Wave Timing
跟随相机的Step-Responsive标题
tsx
const dist = Math.sqrt(Math.pow(r - centerRow, 2) + Math.pow(c - centerCol, 2));
const delay = 30 + dist * 2; // Ripple outward from center
<StaggeredMotion transition={{ opacity: [0, 1], scale: [0, 1], delay, duration: 40 }}>
<div style={{ width: size, height: size, background: color }} />
</StaggeredMotion>tsx
<StepResponsive
centered
style={{ fontSize: vmin * 10, position: 'absolute', width: 'max-content' }}
steps={{
'intro': { transform: [startPos] },
'main': { transform: [mainPos] },
'outro': { transform: [mainPos, endPos], duration: "step", easing: "easeInOutCubic" },
}}
>
<h1>章节标题</h1>
</StepResponsive>Exit Transitions on Steps
波浪式动画的网格
tsx
<Step id="transitions" duration={120} {...pos.toProps()}
exitTransition={{ blur: [0, 10], opacity: [1, 0] }}>
{/* Content blurs out when camera leaves */}
</Step>tsx
const dist = Math.sqrt(Math.pow(r - centerRow, 2) + Math.pow(c - centerCol, 2));
const delay = 30 + dist * 2; // 从中心向外的波浪式延迟
<StaggeredMotion transition={{ opacity: [0, 1], scale: [0, 1], delay, duration: 40 }}>
<div style={{ width: size, height: size, background: color }} />
</StaggeredMotion>Element3D with Transform Keyframes
带退场动画的Step
tsx
<Element3D centered
style={{ width: vmin * 32, height: vmin * 24 }}
transition={{
delay: 10,
opacity: [0, 1],
duration: 35,
transform: [
base.translate(0, vmin * 20, 0), // Start below
base.translate(0, 0, 0), // End at position
],
easing: 'easeInOutCubic',
}}>
<div>slides up into place</div>
</Element3D>tsx
<Step id="transitions" duration={120} {...pos.toProps()}
exitTransition={{ blur: [0, 10], opacity: [1, 0] }}>
{/* 相机离开时内容模糊淡出 */}
</Step>Reference Documentation
带变换关键帧的Element3D
For exhaustive API details:
- references/components.md - Complete component props tables
- references/utilities.md - All utility functions
- references/patterns.md - More animation recipes and examples
tsx
<Element3D centered
style={{ width: vmin * 32, height: vmin * 24 }}
transition={{
delay: 10,
opacity: [0, 1],
duration: 35,
transform: [
base.translate(0, vmin * 20, 0), // 起始位置在下方
base.translate(0, 0, 0), // 结束位置
],
easing: 'easeInOutCubic',
}}>
<div>向上滑动到位</div>
</Element3D>—
参考文档
—
如需完整的API细节:
- references/components.md - 完整的组件属性表
- references/utilities.md - 所有工具函数
- references/patterns.md - 更多动画示例与技巧