remotion-composition
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRemotion Composition
Remotion合成
Generates composition structure documents that define how scenes are ordered, timed, and transitioned in a Remotion video. This skill focuses exclusively on Sequence layout and timing orchestration.
生成合成结构文档,定义Remotion视频中场景的排序、计时和过渡方式。本技能仅专注于Sequence布局和时间编排。
What This Skill Does
本技能的功能
Generates composition structure for:
- Sequence layout — Ordering and positioning of scene Sequences
- Timing calculations — Start frames, end frames, duration for each scene
- Scene transitions — Overlap and crossfade timing between scenes
- Duration mapping — Converting seconds to frames for all scenes
- Timing constants — Structured timing object for constants.ts
为以下内容生成合成结构:
- Sequence布局 — 场景Sequence的排序与定位
- 时间计算 — 每个场景的起始帧、结束帧和时长
- 场景过渡 — 场景间的重叠与淡入淡出计时
- 时长映射 — 将所有场景的秒数转换为帧数
- 时间常量 — 为constants.ts生成结构化的时间对象
Scope Boundaries
范围边界
IN SCOPE:
- Sequence component organization
- Frame calculations for scene timing
- Scene overlap and transition timing
- Duration constant generation
- Scene ordering logic
OUT OF SCOPE:
- Scene component implementation (use )
/remotion-component-gen - Animation parameters (use )
/remotion-animation - Visual styling (colors, layouts)
- Asset management (use )
/remotion-asset-coordinator
包含范围:
- Sequence组件组织
- 场景计时的帧计算
- 场景重叠与过渡计时
- 时长常量生成
- 场景排序逻辑
排除范围:
- 场景组件实现(使用)
/remotion-component-gen - 动画参数(使用)
/remotion-animation - 视觉样式(颜色、布局)
- 资源管理(使用)
/remotion-asset-coordinator
Input/Output Formats
输入/输出格式
Input Format: Scene List with Durations
输入格式:带时长的场景列表
Accepts scene timing specifications:
Natural Language:
Scene 1 (Intro): 0-5 seconds
Scene 2 (Features): 5-15 seconds
Scene 3 (Demo): 15-25 seconds
Scene 4 (CTA): 25-30 secondsStructured Format:
markdown
undefined接受场景计时规范:
自然语言格式:
场景1(开场):0-5秒
场景2(功能介绍):5-15秒
场景3(演示):15-25秒
场景4(行动号召):25-30秒结构化格式:
markdown
undefinedScene Timing
场景计时
Total Duration: 30 seconds
Frame Rate: 30 fps
Total Frames: 900
Scenes:
- Scene 1 - Intro: 5 seconds (0s - 5s)
- Scene 2 - Features: 10 seconds (5s - 15s)
- Scene 3 - Demo: 10 seconds (15s - 25s)
- Scene 4 - CTA: 5 seconds (25s - 30s)
Transitions:
- Fade transition between scenes: 0.5 seconds (15 frames)
undefined总时长: 30秒
帧率: 30 fps
总帧数: 900
场景:
- 场景1 - 开场:5秒(0s - 5s)
- 场景2 - 功能介绍:10秒(5s - 15s)
- 场景3 - 演示:10秒(15s - 25s)
- 场景4 - 行动号召:5秒(25s - 30s)
过渡效果:
- 场景间淡入淡出过渡:0.5秒(15帧)
undefinedOutput Format: COMPOSITION_STRUCTURE.md
输出格式:COMPOSITION_STRUCTURE.md
Generates composition structure document:
markdown
undefined生成合成结构文档:
markdown
undefinedComposition Structure: ProductDemo
合成结构:产品演示
Status
状态
✅ Sequence layout defined
✅ Timing calculations complete
⏳ Ready for scene implementation
✅ Sequence布局已定义
✅ 时间计算已完成
⏳ 待场景实现
Composition Overview
合成概述
Total Duration: 30 seconds (900 frames @ 30fps)
Scenes: 4
Transitions: Crossfade (15 frames)
总时长: 30秒(900帧 @ 30fps)
场景数量: 4个
过渡效果: 淡入淡出(15帧)
Scene Timing Constants
场景时间常量
typescript
const FPS = 30;
export const SCENE_TIMING = {
intro: {
start: 0,
end: 150,
duration: 150,
// 0s - 5s
},
features: {
start: 150,
end: 450,
duration: 300,
// 5s - 15s
},
demo: {
start: 450,
end: 750,
duration: 300,
// 15s - 25s
},
cta: {
start: 750,
end: 900,
duration: 150,
// 25s - 30s
},
} as const;
// Transition timing
export const TRANSITIONS = {
crossfadeDuration: 15, // frames (0.5 seconds)
} as const;typescript
const FPS = 30;
export const SCENE_TIMING = {
intro: {
start: 0,
end: 150,
duration: 150,
// 0s - 5s
},
features: {
start: 150,
end: 450,
duration: 300,
// 5s - 15s
},
demo: {
start: 450,
end: 750,
duration: 300,
// 15s - 25s
},
cta: {
start: 750,
end: 900,
duration: 150,
// 25s - 30s
},
} as const;
// 过渡计时
export const TRANSITIONS = {
crossfadeDuration: 15, // 帧数(0.5秒)
} as const;Composition Layout
合成布局
Main composition with Sequence structure:
typescript
import { AbsoluteFill, Sequence } from "remotion";
import { SCENE_TIMING } from "./constants";
import { Scene1Intro } from "./scenes/Scene1Intro";
import { Scene2Features } from "./scenes/Scene2Features";
import { Scene3Demo } from "./scenes/Scene3Demo";
import { Scene4CTA } from "./scenes/Scene4CTA";
export function ProductDemo() {
return (
<AbsoluteFill>
{/* Scene 1: Intro (0s - 5s) */}
<Sequence
from={SCENE_TIMING.intro.start}
durationInFrames={SCENE_TIMING.intro.duration}
>
<Scene1Intro />
</Sequence>
{/* Scene 2: Features (5s - 15s) */}
<Sequence
from={SCENE_TIMING.features.start}
durationInFrames={SCENE_TIMING.features.duration}
>
<Scene2Features />
</Sequence>
{/* Scene 3: Demo (15s - 25s) */}
<Sequence
from={SCENE_TIMING.demo.start}
durationInFrames={SCENE_TIMING.demo.duration}
>
<Scene3Demo />
</Sequence>
{/* Scene 4: CTA (25s - 30s) */}
<Sequence
from={SCENE_TIMING.cta.start}
durationInFrames={SCENE_TIMING.cta.duration}
>
<Scene4CTA />
</Sequence>
</AbsoluteFill>
);
}带有Sequence结构的主合成:
typescript
import { AbsoluteFill, Sequence } from "remotion";
import { SCENE_TIMING } from "./constants";
import { Scene1Intro } from "./scenes/Scene1Intro";
import { Scene2Features } from "./scenes/Scene2Features";
import { Scene3Demo } from "./scenes/Scene3Demo";
import { Scene4CTA } from "./scenes/Scene4CTA";
export function ProductDemo() {
return (
<AbsoluteFill>
{/* 场景1:开场(0s - 5s) */}
<Sequence
from={SCENE_TIMING.intro.start}
durationInFrames={SCENE_TIMING.intro.duration}
>
<Scene1Intro />
</Sequence>
{/* 场景2:功能介绍(5s - 15s) */}
<Sequence
from={SCENE_TIMING.features.start}
durationInFrames={SCENE_TIMING.features.duration}
>
<Scene2Features />
</Sequence>
{/* 场景3:演示(15s - 25s) */}
<Sequence
from={SCENE_TIMING.demo.start}
durationInFrames={SCENE_TIMING.demo.duration}
>
<Scene3Demo />
</Sequence>
{/* 场景4:行动号召(25s - 30s) */}
<Sequence
from={SCENE_TIMING.cta.start}
durationInFrames={SCENE_TIMING.cta.duration}
>
<Scene4CTA />
</Sequence>
</AbsoluteFill>
);
}Scene Timing Breakdown
场景计时明细
| Scene | Name | Duration | Frames | Start Frame | End Frame |
|---|---|---|---|---|---|
| 1 | Intro | 5s | 150 | 0 | 150 |
| 2 | Features | 10s | 300 | 150 | 450 |
| 3 | Demo | 10s | 300 | 450 | 750 |
| 4 | CTA | 5s | 150 | 750 | 900 |
Total: 30 seconds (900 frames)
| 场景 | 名称 | 时长 | 帧数 | 起始帧 | 结束帧 |
|---|---|---|---|---|---|
| 1 | 开场 | 5s | 150 | 0 | 150 |
| 2 | 功能介绍 | 10s | 300 | 150 | 450 |
| 3 | 演示 | 10s | 300 | 450 | 750 |
| 4 | 行动号召 | 5s | 150 | 750 | 900 |
总计: 30秒(900帧)
Timeline Visualization
时间轴可视化
Frame: 0 150 450 750 900
Time: 0s 5s 15s 25s 30s
|---------|---------|---------|---------|
Scene: | Intro | Features| Demo | CTA |
|---------|---------|---------|---------|帧: 0 150 450 750 900
时间: 0s 5s 15s 25s 30s
|---------|---------|---------|---------|
场景: | 开场 | 功能介绍| 演示 | 行动号召 |
|---------|---------|---------|---------|Next Steps
后续步骤
- Implement scenes via
/remotion-component-gen - Add transitions if needed (crossfades, wipes)
- Integrate constants into composition constants.ts
- Test timing in Remotion preview
- Adjust durations if scenes feel too fast/slow
- 实现场景 通过
/remotion-component-gen - 添加过渡效果 (如淡入淡出、擦除)(若需要)
- 整合常量 到合成文件constants.ts
- 测试计时 在Remotion预览中
- 调整时长 若场景节奏过快/过慢
Checklist
检查清单
- Scene timing calculated
- Sequence layout defined
- Constants generated
- Timing constants structured
- Scene components implemented (next step)
- Transitions added (if needed)
- Timing tested in preview
undefined- 场景计时已计算
- Sequence布局已定义
- 常量已生成
- 时间常量已结构化
- 场景组件已实现(下一步)
- 过渡效果已添加(若需要)
- 计时已在预览中测试
undefinedComposition Patterns
合成模式
Pattern 1: Sequential Scenes (No Overlap)
模式1:顺序场景(无重叠)
Standard sequential layout where scenes don't overlap:
typescript
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>
<Sequence from={450} durationInFrames={300}>
<Scene3 />
</Sequence>标准顺序布局,场景间无重叠:
typescript
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>
<Sequence from={450} durationInFrames={300}>
<Scene3 />
</Sequence>Pattern 2: Overlapping Scenes (Crossfade)
模式2:重叠场景(淡入淡出)
Scenes overlap for smooth transitions:
typescript
const CROSSFADE = 15; // frames
// Scene 1: Full duration
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
// Scene 2: Starts before Scene 1 ends
<Sequence from={150 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
<Scene2 />
</Sequence>
// Scene 3: Starts before Scene 2 ends
<Sequence from={450 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
<Scene3 />
</Sequence>场景重叠实现平滑过渡:
typescript
const CROSSFADE = 15; // 帧数
// 场景1:完整时长
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
// 场景2:在场景1结束前开始
<Sequence from={150 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
<Scene2 />
</Sequence>
// 场景3:在场景2结束前开始
<Sequence from={450 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
<Scene3 />
</Sequence>Pattern 3: Layered Composition
模式3:分层合成
Background + foreground scenes running simultaneously:
typescript
{/* Background layer - runs full duration */}
<Sequence from={0} durationInFrames={900}>
<BackgroundScene />
</Sequence>
{/* Foreground scenes - sequential */}
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>背景+前景场景同时运行:
typescript
{/* 背景层 - 全程运行 */}
<Sequence from={0} durationInFrames={900}>
<BackgroundScene />
</Sequence>
{/* 前景场景 - 顺序播放 */}
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>Pattern 4: Nested Sequences
模式4:嵌套Sequence
Sub-scenes within main scenes:
typescript
<Sequence from={0} durationInFrames={300}>
<AbsoluteFill>
{/* Sub-scene 1 */}
<Sequence from={0} durationInFrames={100}>
<Intro />
</Sequence>
{/* Sub-scene 2 */}
<Sequence from={100} durationInFrames={200}>
<MainContent />
</Sequence>
</AbsoluteFill>
</Sequence>主场景内包含子场景:
typescript
<Sequence from={0} durationInFrames={300}>
<AbsoluteFill>
{/* 子场景1 */}
<Sequence from={0} durationInFrames={100}>
<Intro />
</Sequence>
{/* 子场景2 */}
<Sequence from={100} durationInFrames={200}>
<MainContent />
</Sequence>
</AbsoluteFill>
</Sequence>Timing Calculation Helpers
时间计算辅助工具
Common frame calculations:
typescript
// Convert seconds to frames
const secondsToFrames = (seconds: number, fps: number = 30): number =>
Math.round(seconds * fps);
// Calculate scene timing
interface SceneTiming {
start: number;
end: number;
duration: number;
}
const calculateSceneTiming = (
startSeconds: number,
durationSeconds: number,
fps: number = 30
): SceneTiming => {
const start = secondsToFrames(startSeconds, fps);
const duration = secondsToFrames(durationSeconds, fps);
const end = start + duration;
return { start, end, duration };
};
// Calculate crossfade overlap
const calculateCrossfade = (
scene1Start: number,
scene1Duration: number,
crossfadeDuration: number
) => ({
scene1: {
from: scene1Start,
durationInFrames: scene1Duration,
},
scene2: {
from: scene1Start + scene1Duration - crossfadeDuration,
durationInFrames: crossfadeDuration, // or more if scene is longer
},
});
// Validate total duration
const validateDuration = (
scenes: SceneTiming[],
expectedTotal: number
): boolean => {
const lastScene = scenes[scenes.length - 1];
return lastScene.end === expectedTotal;
};常用帧计算:
typescript
// 将秒数转换为帧数
const secondsToFrames = (seconds: number, fps: number = 30): number =>
Math.round(seconds * fps);
// 计算场景计时
interface SceneTiming {
start: number;
end: number;
duration: number;
}
const calculateSceneTiming = (
startSeconds: number,
durationSeconds: number,
fps: number = 30
): SceneTiming => {
const start = secondsToFrames(startSeconds, fps);
const duration = secondsToFrames(durationSeconds, fps);
const end = start + duration;
return { start, end, duration };
};
// 计算淡入淡出重叠
const calculateCrossfade = (
scene1Start: number,
scene1Duration: number,
crossfadeDuration: number
) => ({
scene1: {
from: scene1Start,
durationInFrames: scene1Duration,
},
scene2: {
from: scene1Start + scene1Duration - crossfadeDuration,
durationInFrames: crossfadeDuration, // 若场景更长则取更大值
},
});
// 验证总时长
const validateDuration = (
scenes: SceneTiming[],
expectedTotal: number
): boolean => {
const lastScene = scenes[scenes.length - 1];
return lastScene.end === expectedTotal;
};Scene Timing Generation
场景计时生成
Automated timing generation from scene list:
typescript
interface SceneSpec {
name: string;
durationSeconds: number;
}
const generateSceneTiming = (
scenes: SceneSpec[],
fps: number = 30
) => {
let currentFrame = 0;
const timing: Record<string, SceneTiming> = {};
for (const scene of scenes) {
const duration = secondsToFrames(scene.durationSeconds, fps);
timing[scene.name] = {
start: currentFrame,
end: currentFrame + duration,
duration,
};
currentFrame += duration;
}
return {
timing,
totalFrames: currentFrame,
totalSeconds: currentFrame / fps,
};
};
// Usage:
const scenes = [
{ name: 'intro', durationSeconds: 5 },
{ name: 'features', durationSeconds: 10 },
{ name: 'demo', durationSeconds: 10 },
{ name: 'cta', durationSeconds: 5 },
];
const result = generateSceneTiming(scenes, 30);
// Result:
// {
// timing: {
// intro: { start: 0, end: 150, duration: 150 },
// features: { start: 150, end: 450, duration: 300 },
// ...
// },
// totalFrames: 900,
// totalSeconds: 30,
// }从场景列表自动生成计时:
typescript
interface SceneSpec {
name: string;
durationSeconds: number;
}
const generateSceneTiming = (
scenes: SceneSpec[],
fps: number = 30
) => {
let currentFrame = 0;
const timing: Record<string, SceneTiming> = {};
for (const scene of scenes) {
const duration = secondsToFrames(scene.durationSeconds, fps);
timing[scene.name] = {
start: currentFrame,
end: currentFrame + duration,
duration,
};
currentFrame += duration;
}
return {
timing,
totalFrames: currentFrame,
totalSeconds: currentFrame / fps,
};
};
// 使用示例:
const scenes = [
{ name: 'intro', durationSeconds: 5 },
{ name: 'features', durationSeconds: 10 },
{ name: 'demo', durationSeconds: 10 },
{ name: 'cta', durationSeconds: 5 },
];
const result = generateSceneTiming(scenes, 30);
// 结果:
// {
// timing: {
// intro: { start: 0, end: 150, duration: 150 },
// features: { start: 150, end: 450, duration: 300 },
// ...
// },
// totalFrames: 900,
// totalSeconds: 30,
// }Transition Patterns
过渡模式
Crossfade Transition
淡入淡出过渡
Smooth opacity crossfade between scenes:
typescript
const CROSSFADE = 15;
// Scene 1 - fades out at end
<Sequence from={0} durationInFrames={150}>
<Scene1 crossfadeOut={CROSSFADE} />
</Sequence>
// Scene 2 - fades in at start
<Sequence from={150 - CROSSFADE} durationInFrames={300}>
<Scene2 crossfadeIn={CROSSFADE} />
</Sequence>
// In Scene component:
function Scene1({ crossfadeOut = 0 }) {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const opacity = crossfadeOut > 0
? interpolate(
frame,
[durationInFrames - crossfadeOut, durationInFrames],
[1, 0],
{ extrapolateRight: 'clamp' }
)
: 1;
return <AbsoluteFill style={{ opacity }}>...</AbsoluteFill>;
}场景间平滑的透明度淡入淡出:
typescript
const CROSSFADE = 15;
// 场景1 - 结束时淡出
<Sequence from={0} durationInFrames={150}>
<Scene1 crossfadeOut={CROSSFADE} />
</Sequence>
// 场景2 - 开始时淡入
<Sequence from={150 - CROSSFADE} durationInFrames={300}>
<Scene2 crossfadeIn={CROSSFADE} />
</Sequence>
// 在场景组件中:
function Scene1({ crossfadeOut = 0 }) {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const opacity = crossfadeOut > 0
? interpolate(
frame,
[durationInFrames - crossfadeOut, durationInFrames],
[1, 0],
{ extrapolateRight: 'clamp' }
)
: 1;
return <AbsoluteFill style={{ opacity }}>...</AbsoluteFill>;
}Hard Cut Transition
硬切过渡
No transition, instant scene change:
typescript
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>无过渡效果,场景瞬间切换:
typescript
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>Slide Transition
滑动过渡
One scene slides out while next slides in:
typescript
const TRANSITION_DURATION = 20;
<Sequence from={0} durationInFrames={150}>
<Scene1 slideOut={TRANSITION_DURATION} />
</Sequence>
<Sequence from={150 - TRANSITION_DURATION} durationInFrames={300}>
<Scene2 slideIn={TRANSITION_DURATION} />
</Sequence>一个场景滑出,同时下一个场景滑入:
typescript
const TRANSITION_DURATION = 20;
<Sequence from={0} durationInFrames={150}>
<Scene1 slideOut={TRANSITION_DURATION} />
</Sequence>
<Sequence from={150 - TRANSITION_DURATION} durationInFrames={300}>
<Scene2 slideIn={TRANSITION_DURATION} />
</Sequence>Duration Validation
时长验证
Ensuring timing adds up correctly:
typescript
// Validation helper
const validateCompositionTiming = (
scenes: Record<string, SceneTiming>,
expectedDuration: number,
fps: number
): { valid: boolean; issues: string[] } => {
const issues: string[] = [];
// Check for gaps
const sceneList = Object.entries(scenes).sort((a, b) => a[1].start - b[1].start);
for (let i = 0; i < sceneList.length - 1; i++) {
const currentEnd = sceneList[i][1].end;
const nextStart = sceneList[i + 1][1].start;
if (nextStart > currentEnd) {
issues.push(`Gap detected: ${currentEnd} to ${nextStart} (${(nextStart - currentEnd) / fps}s)`);
}
if (nextStart < currentEnd) {
issues.push(`Overlap detected: ${sceneList[i][0]} and ${sceneList[i + 1][0]}`);
}
}
// Check total duration
const lastScene = sceneList[sceneList.length - 1][1];
if (lastScene.end !== expectedDuration) {
issues.push(
`Total duration mismatch: expected ${expectedDuration}, got ${lastScene.end} (${lastScene.end / fps}s)`
);
}
return {
valid: issues.length === 0,
issues,
};
};确保计时总和正确:
typescript
// 验证辅助工具
const validateCompositionTiming = (
scenes: Record<string, SceneTiming>,
expectedDuration: number,
fps: number
): { valid: boolean; issues: string[] } => {
const issues: string[] = [];
// 检查间隙
const sceneList = Object.entries(scenes).sort((a, b) => a[1].start - b[1].start);
for (let i = 0; i < sceneList.length - 1; i++) {
const currentEnd = sceneList[i][1].end;
const nextStart = sceneList[i + 1][1].start;
if (nextStart > currentEnd) {
issues.push(`检测到间隙:${currentEnd} 到 ${nextStart}(${(nextStart - currentEnd) / fps}秒)`);
}
if (nextStart < currentEnd) {
issues.push(`检测到重叠:${sceneList[i][0]} 和 ${sceneList[i + 1][0]}`);
}
}
// 检查总时长
const lastScene = sceneList[sceneList.length - 1][1];
if (lastScene.end !== expectedDuration) {
issues.push(
`总时长不匹配:预期 ${expectedDuration},实际 ${lastScene.end}(${lastScene.end / fps}秒)`
);
}
return {
valid: issues.length === 0,
issues,
};
};Timeline Visualization Helper
时间轴可视化辅助工具
Generate ASCII timeline:
typescript
const generateTimeline = (
scenes: Record<string, SceneTiming>,
fps: number,
width: number = 60
) => {
const lastScene = Object.values(scenes).reduce((max, scene) =>
scene.end > max ? scene.end : max, 0
);
const timeline: string[] = [];
// Frame markers
const frameMarkers = Array.from({ length: width + 1 }, (_, i) => {
const frame = Math.round((i / width) * lastScene);
return frame.toString().padStart(4);
}).join('');
timeline.push('Frame: ' + frameMarkers);
// Time markers
const timeMarkers = Array.from({ length: width + 1 }, (_, i) => {
const time = ((i / width) * lastScene) / fps;
return time.toFixed(1) + 's';
}).join(' ');
timeline.push('Time: ' + timeMarkers);
// Scene bars
for (const [name, timing] of Object.entries(scenes)) {
const startPos = Math.round((timing.start / lastScene) * width);
const endPos = Math.round((timing.end / lastScene) * width);
const bar = ' '.repeat(startPos) + '|' + '='.repeat(endPos - startPos - 1) + '|';
timeline.push(`${name.padEnd(8)}: ${bar}`);
}
return timeline.join('\n');
};生成ASCII时间轴:
typescript
const generateTimeline = (
scenes: Record<string, SceneTiming>,
fps: number,
width: number = 60
) => {
const lastScene = Object.values(scenes).reduce((max, scene) =>
scene.end > max ? scene.end : max, 0
);
const timeline: string[] = [];
// 帧标记
const frameMarkers = Array.from({ length: width + 1 }, (_, i) => {
const frame = Math.round((i / width) * lastScene);
return frame.toString().padStart(4);
}).join('');
timeline.push('帧:' + frameMarkers);
// 时间标记
const timeMarkers = Array.from({ length: width + 1 }, (_, i) => {
const time = ((i / width) * lastScene) / fps;
return time.toFixed(1) + 's';
}).join(' ');
timeline.push('时间:' + timeMarkers);
// 场景条
for (const [name, timing] of Object.entries(scenes)) {
const startPos = Math.round((timing.start / lastScene) * width);
const endPos = Math.round((timing.end / lastScene) * width);
const bar = ' '.repeat(startPos) + '|' + '='.repeat(endPos - startPos - 1) + '|';
timeline.push(`${name.padEnd(8)}: ${bar}`);
}
return timeline.join('\n');
};Best Practices
最佳实践
Timing Guidelines
计时指南
typescript
// Minimum scene duration for readability
const MIN_SCENE_DURATION = 30; // 1 second at 30fps
// Standard transition duration
const STANDARD_TRANSITION = 15; // 0.5 seconds
// Maximum scene duration before pacing feels slow
const MAX_SCENE_DURATION = 600; // 20 seconds
// Recommended scene duration range
const IDEAL_SCENE_DURATION = {
min: 60, // 2 seconds
max: 300, // 10 seconds
};typescript
// 保证可读性的最小场景时长
const MIN_SCENE_DURATION = 30; // 30fps下为1秒
// 标准过渡时长
const STANDARD_TRANSITION = 15; // 0.5秒
// 节奏变慢前的最大场景时长
const MAX_SCENE_DURATION = 600; // 20秒
// 推荐的场景时长范围
const IDEAL_SCENE_DURATION = {
min: 60, // 2秒
max: 300, // 10秒
};Composition Organization
合成组织
typescript
// Group related Sequences
// Good:
<>
{/* Background layer */}
<Sequence from={0} durationInFrames={900}>
<Background />
</Sequence>
{/* Content scenes */}
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>
</>
// Bad: Mixed layers without organizationtypescript
// 分组相关的Sequence
// 推荐:
<>
{/* 背景层 */}
<Sequence from={0} durationInFrames={900}>
<Background />
</Sequence>
{/* 内容场景 */}
<Sequence from={0} durationInFrames={150}>
<Scene1 />
</Sequence>
<Sequence from={150} durationInFrames={300}>
<Scene2 />
</Sequence>
</>
// 不推荐:无组织地混合层级Integration Workflow
集成工作流
- Define scene durations → Input to this skill
- Generate composition structure → COMPOSITION_STRUCTURE.md
- Add to composition file (index.tsx)
- Add timing to constants (constants.ts)
- Implement scenes via
/remotion-component-gen - Test timing in preview
- Adjust if needed and regenerate
- 定义场景时长 → 作为本技能的输入
- 生成合成结构 → 输出COMPOSITION_STRUCTURE.md
- 添加到合成文件(index.tsx)
- 将计时添加到常量文件(constants.ts)
- 实现场景 通过
/remotion-component-gen - 在预览中测试计时
- 按需调整并重新生成
Integration with Other Skills
与其他技能的集成
This skill coordinates with:
remotion-composition (this skill)
↓ outputs: COMPOSITION_STRUCTURE.md
remotion-component-gen
↓ implements scenes with timing awareness
remotion-animation
↓ animation timing works within scene durationsWorks with:
- — Scene timing from design specs
/motion-designer - — Structure added to composition file
/remotion-scaffold - — Timing coordinates with animation configs
/remotion-animation - — Scenes fit within calculated durations
/remotion-component-gen - — Orchestrates this skill in pipeline
/remotion-spec-translator
This skill provides precise composition structure and timing calculations that ensure smooth, well-paced Remotion videos.
本技能与以下技能协同工作:
remotion-composition(本技能)
↓ 输出:COMPOSITION_STRUCTURE.md
remotion-component-gen
↓ 实现具备计时感知的场景
remotion-animation
↓ 动画计时与场景时长匹配适配技能:
- — 从设计规范获取场景计时
/motion-designer - — 将结构添加到合成文件
/remotion-scaffold - — 计时与动画配置协同
/remotion-animation - — 场景适配计算出的时长
/remotion-component-gen - — 在流程中编排本技能
/remotion-spec-translator
本技能提供精准的合成结构和时间计算,确保Remotion视频流畅、节奏合理。