📚 Remotion シリーズ 全8回の記事一覧
- 第1回: 全体像をまとめる
Composition / AbsoluteFill / Sequence で動画の骨組みを作る - 第2回: Hooks を理解する
useCurrentFrame / useVideoConfig でフレーム番号と設定を取得する - 第3回: アニメーションを作る
interpolate / spring でフレーム番号を CSS の値に変換する - ▶ 第4回: 素材を使う(この記事)
画像・動画・音楽素材を組み込む - 第5回: テロップを入れる
テキストオーバーレイの作り方 - 第6回: シーンを構成する
Series / TransitionSeries でシーン切り替えとトランジション - 第7回: GitHub Actions で自動レンダリング
CI/CD で動画レンダリングを自動化する - 第8回: 応用テクニック(準備中)
GIF・静止画書き出し / FFmpeg カスタマイズ / Lambda / 3D
はじめに
前回までで、Remotion の基本的な仕組みを一通り見てきました。
- Composition / AbsoluteFill / Sequence で動画の骨組みを作る(第1回)
- useCurrentFrame / useVideoConfig でフレーム番号と設定を取得する(第2回)
- interpolate / spring でフレーム番号を CSS の値に変換する(第3回)
ここまではテンプレートのコードを読み解く話でしたが、ここからは 実際に自分の動画を作る ための実践的な機能に入ります。まずは最も基本的な「手元の画像や動画ファイルを Remotion で使う方法」から見ていきましょう。
public フォルダと staticFile
ファイルの置き場所
Remotion で画像・動画・音声ファイルを使うには、プロジェクトの public/ フォルダに置きます。
my-video/ ├─ public/ │ ├─ background.jpg ← 背景画像 │ ├─ intro-clip.mp4 ← 動画素材 │ ├─ bgm.mp3 ← BGM │ ├─ logo.png ← ロゴ │ └─ fonts/ │ └─ NotoSansJP.woff2 ← フォントファイル ├─ src/ │ ├─ Root.tsx │ └─ ... ├─ package.json
public/ は remotion 依存がある package.json と同じ階層に置く必要があります。
staticFile() でパスを解決する
public/ のファイルを参照するには staticFile() 関数を使います。
import { staticFile } from "remotion"; const logoUrl = staticFile("/logo.png"); // → "/logo.png" のような URL 文字列を返す
staticFile() は 単なるパス変換関数 で、引数のファイル名から正しい URL を生成するだけです。Python でいうと pathlib.Path("public") / "logo.png" のイメージですね。
リモート URL も使える
staticFile() を使わずに、インターネット上のファイルを直接指定することもできます。
<Img src="https://example.com/photo.jpg" /> <Video src="https://example.com/clip.mp4" />
Remotion 専用コンポーネント
画像を表示するだけなら HTML の <img> タグでもいいように思えますよね。動画なら <video>。
でも Remotion では
<Img><Video><Audio>
という専用コンポーネントを使います。
理由は レンダリングの確実性 にあります。
問題:レンダリング時に素材が真っ白になる
Remotion が動画をレンダリング(npx remotion render)するとき、内部的にはヘッドレスブラウザで1フレームずつ画面を画像としてキャプチャし、最後にそれを動画に合成していきます。
もし素材の読み込みが完了する前にキャプチャされたら、そのフレームは素材が表示されていない状態(真っ白や黒背景)になってしまいます。
Remotion 版は素材の準備完了を保証する
<Img> は内部で「この画像がロードされるまでフレームのレンダリングを止める」という仕組みを持っています。
<Video> と <Audio> はさらに踏み込んでいて、レンダリング時に Mediabunny という仕組みで「そのフレームに対応する映像・音声」を正確に抽出します。<Video> の場合は抽出したフレームを <canvas> に描画するので、ブラウザの動画再生に頼らず確実に正しいフレームが表示されます。
まぁ、一旦難しいことは置いておいて、専用のタグを使えばいいと思いましょう!
使い分けまとめ
| やりたいこと | 使うもの | import 元 |
|---|---|---|
| 画像を表示 | <Img> |
remotion |
| 動画を埋め込み | <Video> |
@remotion/media |
| 音声を再生 | <Audio> |
@remotion/media |
また、サーバーサイドレンダリングで正確なフレーム抽出が必要な場合は <OffthreadVideo>(remotion から import)という選択肢もあるようです。
画像を表示する:<Img>
基本的な使い方
import { Img, staticFile } from "remotion"; export const MyScene: React.FC = () => { return <Img src={staticFile("/background.jpg")} />; };
これだけで public/background.jpg が表示されます。
サイズと位置の指定
CSS の style でサイズと位置を制御します。
<Img src={staticFile("/logo.png")} style={{ width: 200, height: 200, position: "absolute", top: 50, left: 50, }} />
画像をキャンバス全体に敷き詰める
背景画像として使いたい場合はこうします。
import { AbsoluteFill, Img, staticFile } from "remotion"; export const BackgroundImage: React.FC = () => { return ( <AbsoluteFill> <Img src={staticFile("/background.jpg")} style={{ width: "100%", height: "100%", objectFit: "cover", }} /> </AbsoluteFill> ); };
objectFit は画像のフィット方法を指定する CSS プロパティです。
| 値 | 動作 | Python 画像処理でいうと |
|---|---|---|
"cover" |
アスペクト比を保ちつつキャンバスを埋める(はみ出た部分はクロップ) | ImageOps.fit() |
"contain" |
アスペクト比を保ちつつキャンバスに収める(余白ができる) | Image.thumbnail() |
"fill" |
アスペクト比を無視して引き伸ばす | Image.resize() |
動画の背景に使うなら cover が一番良さそうです。
画像にアニメーションをつける
<Img> も React コンポーネントなので、style にフレーム番号から計算した値を入れればアニメーションできます。
import { AbsoluteFill, Img, staticFile, useCurrentFrame, interpolate } from "remotion"; export const ZoomingBackground: React.FC = () => { const frame = useCurrentFrame(); // 0フレーム目: 100% → 150フレーム目: 120% にゆっくりズーム const scale = interpolate(frame, [0, 150], [1, 1.2]); return ( <AbsoluteFill> <Img src={staticFile("/landscape.jpg")} style={{ width: "100%", height: "100%", objectFit: "cover", transform: `scale(${scale})`, }} /> </AbsoluteFill> ); };
frame: 0 ------- 150
scale: 1.0 1.2
↑ じわじわとズームイン
これは写真のスライドショーでよく見る「Ken Burns 効果」です。静止画にゆっくりズームをかけることで、動きがないはずの写真に動画的な演出が加わります。
interpolate の入出力を変えれば、パン(横スクロール)も作れます。
const translateX = interpolate(frame, [0, 150], [0, -100]); style={{ transform: `translateX(${translateX}px)` }}
動的なパス:連番画像
テンプレートリテラルでフレームごとに画像を切り替えられます。
const frame = useCurrentFrame(); // フレームごとに異なる画像を表示(パラパラ漫画) <Img src={staticFile(`/frames/frame${frame}.png`)} />
Python でいう f"frames/frame{frame}.png" と同じです。
public/frames/ に frame0.png, frame1.png, ... と連番ファイルを置いておけば、フレーム番号に対応した画像が自動で表示されます。
動画を埋め込む:<Video>
パッケージのインストール
<Video> は @remotion/media パッケージに入っています。プロジェクト作成直後にはないので、最初にインストールが必要です。
npx remotion add @remotion/media
基本的な使い方
import { Video } from "@remotion/media"; import { staticFile } from "remotion"; export const MyScene: React.FC = () => { return <Video src={staticFile("/intro-clip.mp4")} />; };
「同期して再生される」とは
Remotion に埋め込んだ動画は、Composition のタイムラインに 完全に同期 して再生されます。
普通の Web ページに <video> を置いた場合、動画は「再生ボタンを押したら自分のペースで再生する」独立した存在ですよね。でも Remotion では、フレーム番号と動画の再生位置が常に一致します。
トリミング:一部だけ使う
30秒の動画素材のうち、2秒目から10秒目だけを使いたい場合はこうします。
const { fps } = useVideoConfig(); <Video src={staticFile("/interview.mp4")} trimBefore={2 * fps} // 最初の2秒をスキップ trimAfter={10 * fps} // 10秒目で終了 />
trimBefore と trimAfter の値は フレーム数 で指定します。「秒 × fps」でフレーム数に変換すると簡単です。
遅延表示:動画を途中から登場させる
Composition の開始と同時ではなく、少し遅れて動画を表示したい場合は <Sequence> で囲みます。
import { Sequence } from "remotion"; import { Video } from "@remotion/media"; const { fps } = useVideoConfig(); <Sequence from={2 * fps}> <Video src={staticFile("/intro-clip.mp4")} /> </Sequence>
以下の記事で説明した通り、<Sequence> の中では useCurrentFrame() がローカル時間(0 から始まる)を返します。<Video> も同じ仕組みで、Sequence の開始をフレーム0として再生を始めます。
サイズと位置
<Video> も style でサイズと位置を制御できます。
<Video src={staticFile("/clip.mp4")} style={{ width: 640, height: 360, position: "absolute", top: 100, left: 200, objectFit: "cover", borderRadius: 16, }} />
borderRadius をつけると角丸動画になります。objectFit は画像と同じように使えます。
音量の制御
// 固定音量(50%) <Video src={staticFile("/clip.mp4")} volume={0.5} /> // 完全に無音にする <Video src={staticFile("/clip.mp4")} muted />
volume にはコールバック関数も渡せます。引数 f はその動画の再生開始からのフレーム数です。
const { fps } = useVideoConfig(); // 最初の1秒でフェードイン <Video src={staticFile("/clip.mp4")} volume={(f) => interpolate(f, [0, 1 * fps], [0, 1], { extrapolateRight: "clamp" }) } />
ここで interpolate が登場するのが面白いですね。前回の記事で opacity のフェードインに使ったのと全く同じ関数を、音量のフェードインにも使えます。
再生速度
// 2倍速 <Video src={staticFile("/clip.mp4")} playbackRate={2} /> // 0.5倍速(スローモーション) <Video src={staticFile("/clip.mp4")} playbackRate={0.5} />
playbackRate={2} にすると、動画は2倍速で再生されます。つまり同じフレーム数の Sequence に入れた場合、動画の2倍の範囲が再生されることになります。
ループ
// 無限ループ <Video src={staticFile("/clip.mp4")} loop />
短い動画素材を繰り返し再生したいときに使います。Composition の durationInFrames が動画の長さより長くても、ループで埋めてくれます。
音声を再生する:<Audio>
<Audio> も <Video> と同じ @remotion/media パッケージに入っています。
基本的な使い方
import { Audio } from "@remotion/media"; import { staticFile } from "remotion"; export const MyScene: React.FC = () => { return <Audio src={staticFile("/bgm.mp3")} />; };
BGM や効果音を Composition のタイムラインに同期して再生できます。画面には何も表示されませんが、<Video> と同じようにフレームに完全同期して再生されます。
主な props
<Audio> の props は <Video> とほぼ同じです。
| prop | 説明 | 例 |
|---|---|---|
volume |
音量(0〜1、またはコールバック関数) | volume={0.3} |
muted |
ミュートする | muted |
loop |
ループ再生する | loop |
playbackRate |
再生速度 | playbackRate={1.5} |
trimBefore |
開始位置(フレーム数) | trimBefore={2 * fps} |
trimAfter |
終了位置(フレーム数) | trimAfter={10 * fps} |
効果音を特定のタイミングで鳴らす
BGM は動画全体に敷くことが多いですが、効果音は特定のタイミングで鳴らしたいですよね。これも <Sequence> で囲むだけです。
import { Sequence } from "remotion"; import { Audio } from "@remotion/media"; const { fps } = useVideoConfig(); // 3秒目に効果音を鳴らす <Sequence from={3 * fps}> <Audio src={staticFile("/se-pop.mp3")} volume={0.5} /> </Sequence>
<Video> の遅延表示と全く同じパターンですね。
まとめ
ここまで見てきて分かったのは、素材の扱いも Remotion らしくシンプル ということです。
| 概念 | 何をするか | ポイント |
|---|---|---|
public/ |
素材ファイルの置き場所 | プロジェクトルート直下 |
staticFile() |
public/ のファイルを URL に変換 |
デプロイ時のパス解決を吸収 |
<Img> |
画像を表示 | HTML の <img> ではなくこちらを使う |
<Video> |
動画を埋め込み | フレーム同期、トリミング、音量制御が可能 |
<Audio> |
音声を再生 | BGM・効果音に。<Video> と同じ props が使える |
一番大事なポイントは なぜ Remotion 専用コンポーネントを使うのか で、レンダリング時に素材の準備完了を保証するためでした。<Img> はロード完了を待ち、<Video> / <Audio> は Mediabunny で正確なフレームの映像・音声を抽出します。HTML ネイティブの <img> や <video> を使うと、レンダリング時に真っ白なフレームが生成される可能性があります。ここだけ押さえておけば、あとは普通の React と同じ感覚で使えますね。
サンプルアニメーションとソースコード
これを組み合わせると、以下みたいな動画は簡単に作ることができます。
コンポーネント部分だけですが、ソースコードも Gist で一応置いておきます
https://gist.github.com/chan-ume/f3aa0bdaa5e86476c9db3c49ea6e55cc
次の第5回は、これらの素材の上に重ねる「テロップ(テキストオーバーレイ)」の作り方を見ていきます。
