remotion-composition

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Remotion 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:
  1. Sequence layout — Ordering and positioning of scene Sequences
  2. Timing calculations — Start frames, end frames, duration for each scene
  3. Scene transitions — Overlap and crossfade timing between scenes
  4. Duration mapping — Converting seconds to frames for all scenes
  5. Timing constants — Structured timing object for constants.ts
为以下内容生成合成结构:
  1. Sequence布局 — 场景Sequence的排序与定位
  2. 时间计算 — 每个场景的起始帧、结束帧和时长
  3. 场景过渡 — 场景间的重叠与淡入淡出计时
  4. 时长映射 — 将所有场景的秒数转换为帧数
  5. 时间常量 — 为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 seconds
Structured Format:
markdown
undefined
接受场景计时规范:
自然语言格式:
场景1(开场):0-5秒
场景2(功能介绍):5-15秒
场景3(演示):15-25秒
场景4(行动号召):25-30秒
结构化格式:
markdown
undefined

Scene Timing

场景计时

Total Duration: 30 seconds Frame Rate: 30 fps Total Frames: 900
Scenes:
  1. Scene 1 - Intro: 5 seconds (0s - 5s)
  2. Scene 2 - Features: 10 seconds (5s - 15s)
  3. Scene 3 - Demo: 10 seconds (15s - 25s)
  4. Scene 4 - CTA: 5 seconds (25s - 30s)
Transitions:
  • Fade transition between scenes: 0.5 seconds (15 frames)
undefined
总时长: 30秒 帧率: 30 fps 总帧数: 900
场景:
  1. 场景1 - 开场:5秒(0s - 5s)
  2. 场景2 - 功能介绍:10秒(5s - 15s)
  3. 场景3 - 演示:10秒(15s - 25s)
  4. 场景4 - 行动号召:5秒(25s - 30s)
过渡效果:
  • 场景间淡入淡出过渡:0.5秒(15帧)
undefined

Output Format: COMPOSITION_STRUCTURE.md

输出格式:COMPOSITION_STRUCTURE.md

Generates composition structure document:
markdown
undefined
生成合成结构文档:
markdown
undefined

Composition 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

场景计时明细

SceneNameDurationFramesStart FrameEnd Frame
1Intro5s1500150
2Features10s300150450
3Demo10s300450750
4CTA5s150750900
Total: 30 seconds (900 frames)
场景名称时长帧数起始帧结束帧
1开场5s1500150
2功能介绍10s300150450
3演示10s300450750
4行动号召5s150750900
总计: 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

后续步骤

  1. Implement scenes via
    /remotion-component-gen
  2. Add transitions if needed (crossfades, wipes)
  3. Integrate constants into composition constants.ts
  4. Test timing in Remotion preview
  5. Adjust durations if scenes feel too fast/slow
  1. 实现场景 通过
    /remotion-component-gen
  2. 添加过渡效果 (如淡入淡出、擦除)(若需要)
  3. 整合常量 到合成文件constants.ts
  4. 测试计时 在Remotion预览中
  5. 调整时长 若场景节奏过快/过慢

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布局已定义
  • 常量已生成
  • 时间常量已结构化
  • 场景组件已实现(下一步)
  • 过渡效果已添加(若需要)
  • 计时已在预览中测试
undefined

Composition 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 organization
typescript
// 分组相关的Sequence
// 推荐:
<>
  {/* 背景层 */}
  <Sequence from={0} durationInFrames={900}>
    <Background />
  </Sequence>

  {/* 内容场景 */}
  <Sequence from={0} durationInFrames={150}>
    <Scene1 />
  </Sequence>
  <Sequence from={150} durationInFrames={300}>
    <Scene2 />
  </Sequence>
</>

// 不推荐:无组织地混合层级

Integration Workflow

集成工作流

  1. Define scene durations → Input to this skill
  2. Generate composition structure → COMPOSITION_STRUCTURE.md
  3. Add to composition file (index.tsx)
  4. Add timing to constants (constants.ts)
  5. Implement scenes via
    /remotion-component-gen
  6. Test timing in preview
  7. Adjust if needed and regenerate
  1. 定义场景时长 → 作为本技能的输入
  2. 生成合成结构 → 输出COMPOSITION_STRUCTURE.md
  3. 添加到合成文件(index.tsx)
  4. 将计时添加到常量文件(constants.ts)
  5. 实现场景 通过
    /remotion-component-gen
  6. 在预览中测试计时
  7. 按需调整并重新生成

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 durations
Works with:
  • /motion-designer
    — Scene timing from design specs
  • /remotion-scaffold
    — Structure added to composition file
  • /remotion-animation
    — Timing coordinates with animation configs
  • /remotion-component-gen
    — Scenes fit within calculated durations
  • /remotion-spec-translator
    — Orchestrates this skill in pipeline

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视频流畅、节奏合理。