組合多個 Composition
學習如何將多個 Composition 合併成一支完整影片,使用 Series、Sequence 或動態組合技巧。
組合多個 Composition
在製作長影片或多場景影片時,你通常會把不同段落分別開發,最後再組合成一支完整的成品。Remotion 提供了幾種靈活的組合方式。
方法一:使用 <Series> 自動排列
<Series> 是最簡潔的方式,它會自動將子元件按順序排列,不需要手動計算每段的起始幀。
import { Series } from "remotion";
import { Intro } from "./scenes/Intro";
import { MainContent } from "./scenes/MainContent";
import { Outro } from "./scenes/Outro";
export const FullVideo: React.FC = () => {
return (
<Series>
<Series.Sequence durationInFrames={90}>
<Intro />
</Series.Sequence>
<Series.Sequence durationInFrames={300}>
<MainContent />
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<Outro />
</Series.Sequence>
</Series>
);
};然後在 Root 中定義 Composition,總長度就是各段之和:
import { Composition } from "remotion";
import { FullVideo } from "./FullVideo";
export const RemotionRoot: React.FC = () => {
return (
<Composition
id="FullVideo"
component={FullVideo}
durationInFrames={450} // 90 + 300 + 60
fps={30}
width={1920}
height={1080}
/>
);
};方法二:使用 <Sequence> 手動指定位置
當你需要更精確地控制每段的起始時間,或讓多段重疊播放時,使用 <Sequence> 更合適:
import { AbsoluteFill, Sequence } from "remotion";
import { TitleCard } from "./scenes/TitleCard";
import { VideoClip } from "./scenes/VideoClip";
import { LowerThird } from "./scenes/LowerThird";
export const FullVideo: React.FC = () => {
return (
<AbsoluteFill>
{/* 標題卡:第 0~60 幀 */}
<Sequence from={0} durationInFrames={60}>
<TitleCard />
</Sequence>
{/* 主要影片片段:第 60~360 幀 */}
<Sequence from={60} durationInFrames={300}>
<VideoClip />
</Sequence>
{/* 下方字幕條:在影片期間,第 90~240 幀疊加顯示 */}
<Sequence from={90} durationInFrames={150}>
<LowerThird text="特別來賓:王小明" />
</Sequence>
</AbsoluteFill>
);
};方法三:從陣列動態生成場景
當場景數量不固定(例如從資料產生影片)時,可以用陣列動態組合:
import { Series } from "remotion";
import { Slide } from "./Slide";
interface Scene {
id: string;
title: string;
body: string;
durationInFrames: number;
}
const scenes: Scene[] = [
{ id: "s1", title: "第一章", body: "內容一", durationInFrames: 120 },
{ id: "s2", title: "第二章", body: "內容二", durationInFrames: 150 },
{ id: "s3", title: "第三章", body: "內容三", durationInFrames: 90 },
];
export const DynamicVideo: React.FC = () => {
return (
<Series>
{scenes.map((scene) => (
<Series.Sequence key={scene.id} durationInFrames={scene.durationInFrames}>
<Slide title={scene.title} body={scene.body} />
</Series.Sequence>
))}
</Series>
);
};動態計算總幀數並傳入 Composition:
const totalFrames = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
export const RemotionRoot: React.FC = () => {
return (
<Composition
id="DynamicVideo"
component={DynamicVideo}
durationInFrames={totalFrames}
fps={30}
width={1920}
height={1080}
/>
);
};方法四:在 Composition 之間加入轉場
搭配 @remotion/transitions 套件,可以在場景切換時加入淡入淡出、滑動等轉場效果:
import { Series } from "remotion";
import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";
import { slide } from "@remotion/transitions/slide";
import { SceneA } from "./SceneA";
import { SceneB } from "./SceneB";
import { SceneC } from "./SceneC";
export const VideoWithTransitions: React.FC = () => {
return (
<TransitionSeries>
<TransitionSeries.Sequence durationInFrames={120}>
<SceneA />
</TransitionSeries.Sequence>
<TransitionSeries.Transition
presentation={fade()}
timing={linearTiming({ durationInFrames: 20 })}
/>
<TransitionSeries.Sequence durationInFrames={150}>
<SceneB />
</TransitionSeries.Sequence>
<TransitionSeries.Transition
presentation={slide({ direction: "from-right" })}
timing={linearTiming({ durationInFrames: 30 })}
/>
<TransitionSeries.Sequence durationInFrames={90}>
<SceneC />
</TransitionSeries.Sequence>
</TransitionSeries>
);
};計算含轉場的總幀數
使用 TransitionSeries 時,轉場期間兩段場景重疊,總幀數需要扣掉轉場長度:
總幀數 = 所有場景幀數之和 - 所有轉場幀數之和
= (120 + 150 + 90) - (20 + 30)
= 360 - 50
= 310 幀
Series vs Sequence 選用建議
| 情境 | 推薦方式 |
|---|---|
| 場景依序不重疊 | <Series> |
| 場景需要精確定位或重疊 | <Sequence> |
| 場景由資料動態產生 | <Series> + map() |
| 場景之間需要轉場效果 | <TransitionSeries> |