Remotion(プログラムによる動画制作)
RemotionはReactコンポーネントをフレーム単位でレンダリングします。各フレームはフレーム番号の純粋関数であるため、アニメーションは決定論的で再レンダリング可能です。
RemotionのAPIはメジャーバージョン間で変更されます。インストールされているバージョン(
npm ls remotion)を確認し、不明なAPIを使用する前に公式ドキュメント(remotion.dev)を参照してください。企業による商用利用の場合は、Remotionのライセンス条項を確認してください。
セットアップ
npx create-video@latest # ブランクテンプレート(TypeScript)を選択
cd my-video && npm install
npx remotion studio # ライブプレビュー
npx remotion render MyComp out/video.mp4
npx remotion still MyComp out/thumb.png --frame=45
メンタルモデル
useCurrentFrame()は現在のフレームを返します(0から始まります)。useVideoConfig()はfps、width、height、durationInFramesを提供します。- 何も自動ではアニメーションしません。フレームからスタイルを計算します。
- CSSトランジション、
setTimeout、Math.random()、Date.now()をアニメーションに使用しないでください。これらはレンダリングの決定論性を破壊します。代わりにremotionのrandom('seed')を使用してください。
コンポジション
// src/Root.tsx
import {Composition} from 'remotion';
import {Explainer} from './Explainer';
export const RemotionRoot = () => (
<Composition
id="Explainer"
component={Explainer}
durationInFrames={30 * 20} // 20秒 @ 30fps
fps={30}
width={1920}
height={1080}
defaultProps={{title: 'Hello'}}
/>
);
アニメーションの基本
import {AbsoluteFill, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig} from 'remotion';
export const Title: React.FC<{text: string}> = ({text}) => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const opacity = interpolate(frame, [0, 15], [0, 1], {extrapolateRight: 'clamp'});
const scale = spring({frame, fps, config: {damping: 200}}); // スムーズ、バウンスなし
return (
<AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}>
<h1 style={{opacity, transform: `scale(${0.9 + 0.1 * scale})`, fontSize: 96}}>{text}</h1>
</AbsoluteFill>
);
};
export const Explainer = () => (
<AbsoluteFill style={{backgroundColor: '#0b1020', color: 'white'}}>
<Sequence from={0} durationInFrames={90}><Title text="イントロ" /></Sequence>
<Sequence from={90} durationInFrames={120}><Title text="主要ポイント" /></Sequence>
</AbsoluteFill>
);
値が範囲を超えて実行されることを望まない限り、interpolate には常に extrapolateLeft/Right: 'clamp' を渡してください。
メディア
import {staticFile, Audio, OffthreadVideo, Img} from 'remotion';
<Audio src={staticFile('voiceover.mp3')} />
<Audio src={staticFile('music.mp3')} volume={0.12} />
<OffthreadVideo src={staticFile('demo.mp4')} />
<Img src={staticFile('logo.png')} />
アセットは public/ に配置し、staticFile() で参照してください。<img> ではなく <Img> を使用すると、レンダリングが画像を待機します。新しいバージョンのRemotionでは更新されたメディアコンポーネントが提供されています。インストールされているバージョンのドキュメントを確認してください。
シーンをナレーションに同期
シーンの長さをハードコードしないでください。オーディオから導出します。
- シーンごとの音声ファイルを生成します(
voiceover-ttsスキル)。 ffprobeで各ファイルの長さを計測し、{id, audio, seconds}を含むscenes.jsonを作成します。- コンポジションで、
Math.ceil((seconds + 0.5) * fps)で変換し、累積オフセットで<Sequence>を配置します。 <Composition>でcalculateMetadataを使用して、そのデータから合計のdurationInFramesを設定します。
ボイスが再録音された場合、ビデオは自動的に再調整されます。
トランジション
@remotion/transitions をインストールし、TransitionSeries を fade()、slide()、wipe() とともに使用します。トランジションは2つのシーンを重ねるため、合計の長さはトランジションの長さだけ短くなります。トランジションは30fpsで12〜20フレームに抑えてください。
フォントとブランド
@remotion/google-fonts または @remotion/fonts を使用してフォントをロードし、最初のフレームの前に準備できるようにします。色、フォント、ロゴを1つの brand.ts にまとめると、リブランドが1ファイルの変更で済みます。
パフォーマンスとレンダリング
- スタジオでプレビューし、CLIからレンダリングします。
--concurrency=50%はCPU使用率を制限します。--codec=h264がデフォルトです。編集用の受け渡しには--codec=proresを使用します。- 重いぼかし、シャドウ、大きなSVGフィルターは遅くなります。そのような場合は、事前レンダリングされたアセットを優先してください。
- ソース映像には
<video>ではなく<OffthreadVideo>を使用すると、フレーム精度の抽出が可能です。 - クイックドラフトには
--scale=0.5と--frames=0-90の範囲を使用します。
よくある間違い
| 間違い | 修正方法 |
|---|---|
| レンダリング時にちらつきや画像が欠落する | <Img>、staticFile、非同期データには delayRender を使用する |
| プレビューとレンダリングでアニメーションが異なる | 非決定論的なコード(time、random)を削除する |
| 異なるロケールでテキストがはみ出す | 明示的な幅を設定し、最も長い文字列でテストする |
| トランジション追加後にビデオが長すぎる | 合計からトランジションフレームを差し引く |
| レンダリングでオーディオが無音になる | ファイルパスを確認し、<Audio> がアクティブな <Sequence> 内にあることを確認する |
タイミングやイージングの選択については、motion-design-craft と組み合わせて使用してください。