Lottieを使ってみた

## はじめに

ジョブカン事業部のアドベントカレンダー10日目の記事です

https://qiita.com/advent-calendar/2024/jobcan

9日目の記事は @Larvesta636 さんによる「日報作成＆工数出力のGUIアプリつくってみた」でした。執筆おつかれさまでした。
開発したツールで月次業務が軽減されると良いですね！


### 概要

本稿はAdobe AfterEffects（以下、AfterEffects）で作成したアニメーションをBodymovinとLottieを使用して、ブラウザー上で再生可能なSVGアニメーションとしてHTMLに埋め込むまでを実施してみます。

### モチベーション

Webページにアニメーションを追加するとUX向上を期待できます。しかし、GIFアニメやMP4などの動画ファイルではファイルサイズが大きくなり、読み込み速度に影響を与えかねません。また、これらの形式はラスター形式であるため拡大するとジャギー（ギザギザ）が目立ちます。

SVGアニメーションなら軽量でギザギザしないスケーラブルなアニメーションを実現できます。が、凝ったアニメーションは実装に時間がかかり、実装しても正しく描画されないし変更が困難と感じるのはその辺りに触れたご経験があってもなくても共感いただけると思います。
そこで、今回はモーショングラフィックでお馴染みのAfterEffectsとそのプラグインを用いたSVGアニメーションを作ってみます。

#### それならFlash後継のAnimateでいいじゃないか

とお叱りを受けそうですが、あちらは正直なところ雲行きが怪しい（本体の開発が鈍化、出力が参照するライブラリーが古め）ので、見なかったことにしました。


#### Lottieなんて聞いたことがない。無名で実績のないライブラリーは怖くて使えない

とも言われそうですが、以下の公式概要ページを見るとAppleやGoogleなど誰もが知っている大手で使用実績があります。

https://lottiefiles.com/what-is-lottie

執筆時現在のAppleはWebページ内の動画にMP4を使用していますが、iOS 18のページを開発者ツールで確認すると `lottie` の文字列を見つけることができます。

#### じゃあMP4やめて全部Lottieにするわ

:::note warn
Bodymovinは万能ではありません

Bodymovinはサポートしていないアニメーション表現を無視して出力します。特にエフェクトはほとんど使用できないため、使用を諦めるか代替手段で実装する必要があります
:::

例えば動画でよく使われるパーティクル（粒子表現）や複雑な3D表現は（執筆時現在では）できません。サポート状況は以下の公式ページに記載がありますが、設定画面とにらめっこしながら実際に出力してみるのがわかりやすいと思います

http://airbnb.io/lottie/#/supported-features



## できたもの

* 約2秒のアニメーションです
* 中央あたりをクリック（タップ）するとアニメーションを再生します
* 再生中にクリックすると一時停止します
* 最後まで再生した状態でクリックすると最初から再生します
* 表示を拡大・縮小してもジャギらないことが実際に確認できると思います

<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="wvVZWxa" data-pen-title="LGTM animation example" data-preview="true" data-user="djwq" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/djwq/pen/wvVZWxa">
  LGTM animation example</a> by djwq (<a href="https://codepen.io/djwq">@djwq</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 前提条件・環境構築

### 必要なツール・ライブラリ

* [AfterEffects](https://www.adobe.com/jp/products/aftereffects.html)
    * 言わずと知れた動画作成ソフト。有償です
* [Bodymovin拡張機能](https://github.com/airbnb/lottie-web)
    * AfterEffectsの動画をJSONとして出力します
* [Lottie Webライブラリ](https://github.com/airbnb/lottie-web)
    * アニメーションが記述されたJSONを制御します
* [We](https://www.google.com/intl/ja_jp/chrome/)[bブ](https://www.mozilla.org/ja/firefox/)[ラウ](https://www.opera.com/ja)[ザー](https://www.apple.com/jp/safari/)
    * 説明不要。モダンブラウザーならデスクトップでもスマートフォンでも可です


### 環境構築手順

1. AfterEffects をインストール
1. Bodymovin拡張機能をインストール
    * [Adobe Exchange](https://exchange.adobe.com/apps/cc/12557)や[LottieのGitHubページ](https://github.com/airbnb/lottie-web)からもDL可能ですが、[aescripts + aeplugins](https://aescripts.com/bodymovin/)（AfterEffects拡張機能のストアサイト）からのDLを公式では推奨しているようです
        * 金額が`$20.00`になっていますが、自分で指定できるので`$0.00`も可能です
        * [manager app](https://aescripts.com/learn/aescripts-aeplugins-manager-app/)があるとインストールが楽です。おすすめです

なお、実行はWebブラウザーで完結するのでNode.jsなどのJavaScript環境は不要です。

## 実装方法

### AfterEffectsでアニメーションを作成

Lottieを使う前にまずアニメーション本体を作成します。

<details>

<summary>詳細な作り方を省いても長くなったので折りたたみました</summary>

1. 新しいコンポジションを作成してサイズとフレームレートを設定します
    * とりあえず400px*400px, 60fpsにしました
1. `LGTM`を配置します。文字は独立してアニメーションさせるので、単一のテキストレイヤーではなくバラバラに配置します
    * フォントは[Quicksand Bold](https://fonts.adobe.com/fonts/quicksand)にしました。いい感じにころんとしていてかわいいです
        * 余談ですが実は[Google Fonts](https://fonts.google.com/specimen/Quicksand)の一部です。知らなかった...
1. 4つのテキストレイヤーそれぞれからシェイプレイヤーを作成します
    * テキストレイヤーのままではユーザー環境にフォントを要求するため、未インストールの環境では何も表示されません(n敗)
    * これを回避するため、テキストデータをベクターデータに変換して図形として扱うようにします
1. アニメーションを設定していきます。この作例は色の変化を除くと`トランスフォーム`で完結します
    * L ... `位置`と`回転`をアニメーションします
    * G ... `回転`を設定しておき`位置`をアニメーションします。落下時は縦長、衝突時に横長になるよう`スケール`を適度に設定します。アニメーション後半で`回転`を戻します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/e7f4b5ae-8b24-8f8b-9838-18c7eb6d257b.png)←落下時　地面衝突時→![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/bccb0287-4be6-31ec-9c67-0cdc24560ea1.png)
    * T ... `アンカーポイント`を中央下に設定しておき`スケール`をアニメーションします。スケールが最高地点になったら`位置`もアニメーションさせます
    * M ... 3Dレイヤーを設定しておき、`Y回転`をアニメーションします。90°を設定してもきれいに隠れないので、回転がはじまるまでは`不透明度`を`0%`にしています
    * 後半でシェイプの`塗り`を`#62A365`に設定します
    * 基本的にキーフレームにはイージーイーズを設定しておきます
    * 必要に応じてグラフエディターで速度調整して動きに緩急をつけます
        * 感覚ですが、速→遅にしておくといい感じになります
    * タイムラインは以下のようになりました。`スケール`をアニメーションしているヌルオブジェクトは、シェイプレイヤーに設定すれば不必要かもしれません
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/ed59dfee-57de-7637-548b-be91ef044d49.png)

</details>


### BodymovinでJSONファイルをエクスポート

AfterEffectsでアニメーションを作ったら、次にJSONファイルへエクスポートします。

1. AfterEffectsのメニューから`ウィンドウ` > `エクステンション` > `Bodymovin`を選択します
1. エクスポートしたいコンポジションにチェックを入れます（独自UIでややわかりにくいですが）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/09888975-f9bf-3683-d0ed-1b3232aed7b9.png)
    * `Settings`を開いて`Demo`にチェックしておくとブラウザーで確認できるHTMLファイルも出力します。`Save`で設定を保存します。以下は設定例です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/44509c34-122c-c4df-5dd0-d8355f3f5347.png)

1. 出力フォルダを指定します
1. `Render`ボタンをクリックして、JSONファイルを出力します
:::note warn
出力フォルダの設定を行いましたか？

手順3も必須です。筆者はこれを忘れて`Render`ボタンが押せず、小一時間溶かしました
:::

出力が成功すると以下のような表示になります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918059/0857a177-526a-8028-f133-0382c39d5828.png)


### HTMLにアニメーションを組み込む

JSONファイルにエクスポートしたら、コードを書いて実際にアニメーションさせます。

:::note info
完成形のコードを見たい

コード全文は上記サンプルの`HTML`, `CSS`, `JS`タブより参照いただけます
:::

#### HTML

Lottieライブラリの読み込みます。
手っ取り早く確認するには[CDNのJS](https://cdnjs.com/libraries/lottie-web)を読み込むのが良いと思います
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>
```

アニメーションを描画する要素を用意します。
サイズはCSSで指定するので、ここでは要素の配置のみとします。JavaScriptから要素を参照するので`id`属性を正しく設定しておくとよいかなと思います
```html
<div id="lottie"></div>
```

#### CSS

描画する要素のサイズを指定します。
スケーラブルなのでいくつでもよいですが、とりあえずコンポジションと同じく400px*400pxにします。ついでに見た目をよくするため`margin`でセンタリングします
```css
div#lottie {
    width: 400px;
    height: 400px;
    margin: 0 auto;
}
```

#### JavaScript

Bodymovinが出力したJSONを記述します。
ファイルからロードできればそれもよいですが、外部サーバーを用意するのは面倒なので愚直に`data.json`を展開します
```js
const data = {...};    // data.jsonの中身
```

続いて初期化をします。アニメーションを描画する要素の決定とアニメーションの読み込みを行います。
特に設定していないと自動再生するので `autoplay: false` を設定します。さらに`loop: false` を設定してループ再生は無効にします（画面がうるさくなるので）
```js
// 描画要素を取得
const lottieContainer = document.querySelector("#lottie");

// アニメーション初期化
const animation = lottie.loadAnimation({
  container: lottieContainer, // 描画する要素
  animationData: data, // エクスポートしたJSON
  autoplay: false, // 自動再生
  loop: false // ループ再生
});
```

描画する要素とアニメーションにコールバック関数を設定します。アニメーションが再生中かの情報は取得できないようなので自前で管理します
```js
// 再生中？
let playing = false;
// 再生完了？
let completed = false;

// 描画要素のクリックイベント
lottieContainer.onclick = () => {
  if (playing) {
    // 再生中なら一時停止
    playing = false;
    animation.pause();
  } else {
    if (completed) {
      // 再生終了済みなら先頭に戻す
      animation.goToAndStop(0);
      completed = false;
    }

    // 再生
    playing = true;
    animation.play();
  }
};

// アニメーション完了イベント
animation.onComplete = () => {
  playing = false;
  completed = true;
};
```

これでCodePenのサンプルと同じ動作をするはずです


## もう少し実用的な例

文字をアニメーションさせるだけでは実用的な使い方のイメージがしにくいかなと思ったので、無限スクロールで使えそうなアニメーションを作ってみました

* 画面中央部をクリックするとアニメーションします
* さらにクリックすると状態遷移します（待機中→処理中→完了→待機中→...）
    * 実際にロードするものがないので、代わりにクリックで擬似的に状態遷移しています

<p class="codepen" data-height="499" data-default-tab="result" data-slug-hash="JjgVmWG" data-pen-title="Loading Sample" data-preview="true" data-user="djwq" style="height: 499px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/djwq/pen/JjgVmWG">
  Loading Sample</a> by djwq (<a href="https://codepen.io/djwq">@djwq</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## まとめ

本稿では、AfterEffectsで作成したアニメーションをBodymovinとLottieを使用してWebページに埋め込む方法を解説しました。今回はWebページでしたが、Lottieは[iOS](https://github.com/airbnb/lottie-ios)や[React](https://github.com/Gamote/lottie-react)（非公式）など様々な環境で動作するよう移植されています。
また、Lottieは逆再生やスクロール位置に応じたアニメーションなど、本稿では紹介していない機能もあります。ぜひご自身のプロジェクトで活用してみてください。

試してみたいけどAfterEffectsなんて持っていない！という各位は以下のページにあるファイルでLottieを試すことができます（要登録）

https://lottiefiles.com/jp/free-animations/

## さいごに

DONUTSでは一緒に働くメンバーを募集しています。
昨年や今年のアドベントカレンダーをご覧になっておもしろそうな人がいる、自分ならプロダクトをもっとよくできるなどジョインしたいという想いがありましたら、以下のページをご覧ください。お待ちしております。

https://www.donuts.ne.jp/recruit/

https://recruit.jobcan.jp/donuts/show/B001/1396297

明日の記事は @imadon2024 さんによる「NetflixのSystem Designを眺めてみた」です。ここまで読んでいただいた各位はNetflixのアーキテクチャーも是非眺めてみてください。

記事は以上です。アドカレ初投稿の鳩(@djwq)でした。