【React】【TypeScript】Remotionでフェードインからフェードアウトする動画を作ってみた

2023/12/16に公開

まえがき

ライブラリRemotionを使ってフェードインから最後にフェードアウトする動画を作ります。

動画でも解説しているので、当記事と一緒にご覧ください。

https://youtu.be/86h8TOOJbkA

Remotionとは

Remotionとはプログラミングで動画を作れるライブラリです。

https://www.remotion.dev/

基本はReactベースでTypeScriptを使います。
また、他にもたくさんのテンプレートが用意されています。

https://www.remotion.dev/templates

TypeScriptではなく、JavaScriptでも使えます。
Reactのフレームワークである、Next JS App Routerも使えます。

プロジェクトを新規作成

今回からVS CodeからCursorというエディタに変更します。

https://cursor.sh/

VS CodeベースでAI機能が付属したようなエディタです。
詳しいことは、他の動画を見ていただきたいと思います。
それではCursorを開いて、ターミナルを呼び出します。
プロジェクトを作成する任意の場所に移動します。
私はデスクトップ上のディレクトリProjectの中にディレクトリRemotionProjectを作成しています。
コマンドcd desktop/project/remotionproject/を実行して移動します。

cd desktop/project/remotionproject/

プロジェクトを作成するディレクトリに移動したら、ターミナルでコマンドnpm init videoを実行します。

npm init video

まずはプロジェクト名を質問されます。
ここではremotion-fade-in-out-ytとします。
ちなみにremotion-fade-in-out-ytは自由に決めていただいて結構です。

次はテンプレートを選択するように指示されます。ここでは、上から4番目のBlankを選択します。

ディレクトリremotion-fade-in-out-ytが作成されて、その中にプロジェクトが作成されます。

プロジェクトが作成されたら、指示通りにディレクトリremotion-fade-in-out-ytに移動します。

cd remotion-fade-in-out-yt

移動したら、新たにCursorを開きます。

code .

Prettierの設定

Prettierはコードを自動的に整形するツールです。
これを利用すると決めたルールに則ってコードを自動的に変更してくれます。

拡張機能「Prettier - Code formatter」をインストール

拡張機能「Prettier - Code formatter」をインストールしておきます。

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Cursorの設定

Cursorの設定を変更します。
メニューからCursor - 基本設定 - 設定を選択します。
ショートカットキーでCommand,で開くこともできます。
こちらの方が早いので、覚えておきましょう。

Cursorの設定にFormat On Saveという項目があります。
Format On Saveを検索します。

検索したら、テキストエディタのFormat On Saveを見ます。
もしチェックが入っていない場合はチェックを入れてください。

次に、default formatと検索します。
検索したら、テキストエディタのEditor: Default Formatterを見ます。
Prettier - Code formatterを選択します。

.prettierrcを変更

私の好みですが、シングルクォートよりダブルクォート、波括弧の中にはスペースを入れるようにルールを変更します。
また、タブ文字を使わずにスペースが使われるようにルールを変更します。

Root.tsx
- import {Composition} from 'remotion';
+ import { Composition } from "remotion";

.prettierrcでルールは設定できます。
早速.prettierrcを編集します。

singleQuote

singleQuoteはシングルクォートを使うかどうかを設定します。
ここではtrueでシングルクォートを使うようになっているのでfalseに変更します。

.prettierrc
- "singleQuote": true,
+ "singleQuote": false,

bracketSpacing

bracketSpacingはスペースを入れるかどうかを設定します。
ここではfalseでスペースを入れないようになっているのでtrueに変更します。

.prettierrc
- "bracketSpacing": false,
+ "bracketSpacing": true,

useTabs

useTabsはタブ文字を使用してインデントを行うか、スペースを使うか設定します。
ここではtrueでタブ文字を使うようになっているのでfalseに変更します。

.prettierrc
- "useTabs": true,
+ "useTabs": false,

これで決めたルール通り、保存するときにコードが自動的に整形されます。

フェードイン

まずは文字列がフェードインする動画を作成します。
今回の本題であるフェードインからフェードアウトの動画を作成するにはinterpolate関数を必要になります。
この関数は指定された入力値の範囲から出力値の範囲への線形補間を行うRemotionライブラリの関数です。
線形補間とは得られているデータを直線でつなげることです。
と、言葉で言ってもよくわからないですよね。
そこでinterpolate関数を使ったフェードインする動画を作成してみます。
まずは実際のコードでinterpolate関数の基本的な使い方を見ていきます。

interpolate関数

ファイルComposition.tsxにコードを追記します。

Composition.tsx
+ import { interpolate, useCurrentFrame } from "remotion";
  
  export const MyComposition = () => {
+   const frame = useCurrentFrame();
+   const opacity = interpolate(frame, [0, 20], [0, 1]);
  
    return null;
  };

interpolate関数に3つの引数を渡しています。
第1引数は入力値を指定します。
ここではframeを渡しています。
第2引数は入力が想定する値の範囲を指定します。
ここではフレーム0からフレーム20を指定しています。
第3引数は入力に対応させたい出力値の範囲を指定します。
ここでは不透明度0から不透明度1を指定しています。
これでフレーム0からフレーム20の間に不透明度0から不透明度1になります。
これを線形補間すると、以下のようなグラフで表されます。

ここまでを踏まえてコードを完成させます。

Root.tsx

Root.tsxはそのまま使います。

Root.tsx
import { Composition } from "remotion";
import { MyComposition } from "./Composition";

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="MyComp"
        component={MyComposition}
        durationInFrames={60}
        fps={30}
        width={1280}
        height={720}
      />
    </>
  );
};

動画のフレーム数はdurationInFrames={60}で設定しています。
これを線形補間すると、以下のようなグラフで表されます。

Composition.tsx

ファイルComposition.tsxにコードを完成させます。
return nullnullの部分をコンポーネントAbsoluteFillを追加します。
interpolate関数以外の詳しい説明は以前のRemotionの記事をご覧ください。
あとは、わかりやすいようにフレーム数も表示するように加えておきます。

Composition.tsx
import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";

export const MyComposition = () => {
  const frame = useCurrentFrame();
  const opacity = interpolate(frame, [0, 20], [0, 1]);

  return (
    <AbsoluteFill
      style={{
        flex: 1,
        backgroundColor: "white",
        justifyContent: "center",
        alignItems: "center",
        fontSize: "7em",
      }}
    >
      <p>{`フレーム数:${frame}`}</p>
      <div style={{ opacity }}>Hello, World</div>
    </AbsoluteFill>
  );
};

プレビューを確認

わかりやすいように再生速度を0.25倍にして再生しています。

できた動画を見るとフレーム0からフレーム20にかけてHello, Worldが徐々に現れてきます。
その後はHello, Worldが表示されつづけます。

フェードインからフェードアウト

フェードイン効果はそのままに、最後にフェードアウト効果を加えます。

interpolate関数

以下のinterpolate関数のコードを変更します。
またフックuseVideoConfigを使ってdurationInFramesを取得します。

Composition.tsx
  import { interpolate, useCurrentFrame, useVideoConfig } from "remotion";

  export const MyComposition = () => {
    const frame = useCurrentFrame();
+   const { durationInFrames } = useVideoConfig();
    const opacity = interpolate(
      frame,
-     [0, 20],
+     [0, 20, durationInFrames - 20, durationInFrames],
    // v--v---v----------------------v
-     [0, 1]
+     [0, 1, 1, 0]
    );
    ...
  }

第2引数[0, 20, durationInFrames - 20, durationInFrames]は4つの時点のフレーム数を指定しています。
そして第3引数[0, 1, 1, 0]は4つの時点に対応した不透明度を指定しています。
これを見ると20フレームから動画が終わる20フレーム前では不透明度は1のままです。
最後には不透明度は0になるはずです。
一度に複数のポイントを補間しuseVideoConfig()を使用してコンポジションの継続時間を決定できます。

Root.tsx

Root.tsxはそのまま使います。
Root.tsxで動画のフレーム数はdurationInFrames={60}で設定していました。
これを線形補間すると、以下のようなグラフで表されます。

Composition.tsx

コードは以下のようになります。
interpolate関数以外はそのままで大丈夫です。

Composition.tsx
import {
  AbsoluteFill,
  interpolate,
  useCurrentFrame,
  useVideoConfig,
} from "remotion";

export const MyComposition = () => {
  const frame = useCurrentFrame();
  const { durationInFrames } = useVideoConfig();
  const opacity = interpolate(
    frame,
    [0, 20, durationInFrames - 20, durationInFrames],
    [0, 1, 1, 0]
  );

  return (
    <AbsoluteFill
      style={{
        flex: 1,
        backgroundColor: "white",
        justifyContent: "center",
        alignItems: "center",
        fontSize: "7em",
      }}
    >
      <p>{`フレーム数:${frame}`}</p>
      <div style={{ opacity }}>Hello, World</div>
    </AbsoluteFill>
  );
};

プレビューを確認

わかりやすいように再生速度を0.5倍にして再生しています。

できた動画を見るとフレーム0からフレーム20にかけてHello, Worldが徐々に現れてきます。
そしてフレーム21からフレーム40の間はHello, Worldが表示されます。
最後にフレーム41からフレーム60にかけてHello, Worldが徐々に消えていきます。

interpolate関数の考え方

ここまでinterpolate関数を線形補間してグラフで2度表示しました。

const opacity = interpolate(
  frame,
  [0, 20, durationInFrames - 20, durationInFrames],
// v--v---v----------------------v
  [0, 1, 1, 0]
);

durationInFrames60とすると以下のようにコードを書き換えられます。

const opacity = interpolate(
  frame,
  [0, 20, 40, 60],
// v--v---v---v
  [0, 1, 1, 0]
);

そして、グラフに表すと以下のようになりました。

コードとグラフを見比べてみます。
第2引数の値はグラフの横軸の値になり、第3引数の値はグラフの縦軸の値になっています。
そこから自分が描きたい動きをグラフで想像して、引数の値を考えるといいかもしれません。

あとがき

今回はライブラリRemotionを使ってフェードインからフェードアウトする動画を作ってみました。
プロジェクトのソースコードはGitHubで公開しています。
参考にしてください。

https://github.com/arafipro/remotion-fade-in-out-yt

GitHubで編集を提案

Discussion