宇宙一かわいいQiitanを作ろう！〜React Lottieで簡単2Dアニメーション〜

こんにちは、[Ateam Brides Inc. Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ateam-brides)の11日目は
株式会社エイチームブライズの:qiitan:**Qiitanかわいい芸人:qiitan:@naomunaomu**が担当します。

<img width="375" alt="fix.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/c14132dd-3316-41fe-373b-c3f4132b05f7.gif">

## :qiitan:これは何

Qiitanってかわいいですよね。
**Qiitanがニコニコ動いていたら、絶対ぜったい、超かわいいですよね？？**
というわけで、**私達のアイドルQiitanを、スペシャルかわいく動かしたいと思います！**

## :qiitan-cry:完成形
LottieとReactを使い、最終的にこのような画面をReact上で実装します。
（サンプルはGIFのため若干カクカクですが、**実際のQiitanは5億倍かわいいです**）
<img width="375" alt="fix.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/c14132dd-3316-41fe-373b-c3f4132b05f7.gif">

「Qiitan、ジャンプして！」等のボタンをクリックすることで、
パチパチとアニメーションと背景が切り替わっていく仕組みです。

Lottieなら簡単かつ軽量に、今すぐあなたのWEBサイトにも導入できますよ！

:::note info 
Lottieとは、Airbnbが開発した、iOSやWeb用のアニメーションライブラリです。
SVGアニメーションとして書き出せるので、軽量かつサイズを自由に変更できます。
:::

:qiitan:　ソースコードはこちら

https://github.com/yamanayama/QiitanApp

[:qiitan:　Demoページはこちら](https://yamanayama.github.io/QiitanApp/)

## :qiitan:使用ツール
- アニメーション作成
  - Illustrator
        - アニメーション素材を作成します。LottieのSVGアニメーションは、PNG等の画像は使用できないので注意!
  - After Effects
        - アニメーションを作成します。作成後、Lottie用プラグイン[Bodymovin](https://exchange.adobe.com/creativecloud.details.12557.html)で書き出します。
- 実装
  - Lottie
        - Reactでは[React-Lottie](https://github.com/chenqingspring/react-lottie)を使います。
  - React
        - みんな大好きな`create-react-app`です。
  - [Emotion](https://emotion.sh/docs/introduction)
        - css Propでアニメーションの切り替えと同時にスタイルを変更します。

それではいってみましょう！


## :qiitan-cry:アニメーションの作成
まずは完成形のアニメーションを。今回はこちらの３点を用意しました。
左から、**ジャンプ / 笑顔 / 怒る**　です。
<img width="208" alt="jump.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/a8a78b4b-f98b-7f60-9d85-9bd710671fd2.gif">  <img width="200" alt="smile.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/9bbf7d46-608f-d1cf-0885-a76a26112669.gif"> <img width="200" alt="angry.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/746ecccd-8194-6fd6-140c-ff5988a37645.gif">

まずはIllustratorでアニメーションの元になるイラストデータを用意します。
動かしたいパーツごとにレイヤーを分けて作成しましょう。

<img width="400" alt="src_ai.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/eac96da0-fc3c-6054-6524-42cdf695a25b.png">
![sc1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/172a86d7-3caf-5ba1-de54-e96581866a56.png)
「eye_right_open」は右目（開）、「eye_left_open」は左目（開）ですね。

この時のレイヤー名・レイヤー構成がそのままAfter Effectsに反映されるため、きれいに整理しておきましょう。

aiデータが用意できたら、After Effectsに読み込みます。


## :qiitan:After Effectsで作成
新規プロジェクトを作成し、新規コンポジションに先程作成したaiデータを読み込みます。
読み込みの種類は「コンポジション」、フッテージサイズは「ドキュメントサイズ」です。

<img width="300" alt="src3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/e8f4b510-6fed-e3a6-429c-72685ab661f7.png">

詳細な作成方法は省きますが、こちらの動画が非常に参考になります。ぜひ見てみてください！
:point_right:参考：[キャラクターが歩く！ウォークサイクルの作り方【After Effects チュートリアル】](
https://www.youtube.com/watch?v=9gPVU2ul8sQ&ab_channel=mooograph)


## :qiitan-cry:Bodymovinで書き出し
BodymovinはAfter Effects用の拡張プラグインです。作成したアニメーションをjson/html形式で書き出してくれます。
完成したらjsonデータに書き出し、Reactで実装していきましょう。

:point_right:ダウンロードはこちら
[Bodymovin](https://exchange.adobe.com/creativecloud.details.12557.html
)https://exchange.adobe.com/creativecloud.details.12557.html

:point_right:Bodymovinの書き出し方法はこちらを参考にしてください。
参考：[After Effectsで作ったアニメーションをreact-lottieで実装する](https://qiita.com/xrxoxcxox/items/3f617a83ec0512d97d75)

## :qiitan:うまく書き出しできない！？そんなときは要チェック！

### :qiitan-cry:SVGで画像が書き出されない！？
- PNGやJPG/PSDの画像を読み込んでいるかも…
    - SVGに変換できず、画像としてそのまま書き出されてしまいます。
    - 必ずaiでベクターデータとして作成しましょう。
- aiデータをそのまま使用しているかも…
    - ベクターデータを読み込んだだけでは、SVGとして書き出されません。**ややこしい**
    - 作成　＞　ベクトルレイヤーからシェイプを作成　で、シェイプに変換しましょう。
    
### :qiitan:エフェクトが表示されない…
- Lottieでは、**基本的にエフェクトは使用できません。**
- 不透明度、回転、拡大縮小は使用できます。特殊なエフェクトは使用できませんので、下記を参考にエフェクトを見直してみてください。
    -  [Lottieで書き出しできる、できないAfterEffectsの機能](https://note.com/nviveto/n/n54fd1776a464)

## :qiitan-cry:Lottieで簡単に実装する

ここからは実際にReact上でLottieを動かしていきます。
まずは、ReactプロジェクトにLottieをインストールしましょう。
今回はTypeScript用に`@types/react-lottie`も追加します。

```
yarn add react-lottie @types/react-lottie
```

https://github.com/chenqingspring/react-lottie

src配下にアニメーション用のフォルダを作成し、先程After Effectsで書き出したjsonデータを格納していきます。

<img width="300" alt="src_aniamtionData.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/381a67bc-094e-a11e-0d59-c3de116c4a4f.png">


### :qiitan:Component

まずはLottie用のコンポーネント`LottieItem.tsx`を作成し、`option`を設定していきます。
`animationData`に、使用するアニメーションデータファイルを指定すると、ページ上でアニメーションが再生されます。**便利〜〜**
今回は`loop`と`animationData`に`props`を渡せるように拡張しました。

```React:LottieItem.tsx
import Lottie, { Options } from "react-lottie";
import { VFC } from "react";

type Props = {
  animation: Options["animationData"];
  loop: boolean;
};

export const LottieItem: VFC<Props> = ({ animation, loop }) => {
  return (
    <Lottie
      options={{
        loop: loop,
        autoplay: true,
        animationData: animation,
        rendererSettings: {
          preserveAspectRatio: "xMidYMid slice",
        },
      }}
      height={375}
      width={360}
      isClickToPauseDisabled={true}
    />
  );
};

```


Lottieデータは実装時、クリックされるとアニメーションが一時停止するのがデフォルトになっています。
これを止めたい場合は、`isClickToPauseDisabled`を`true`で渡してあげましょう。

`preserveAspectRatio`はアスペクト比が入ります。
sliceはボックスからはみ出したアニメーションをトリミングします。

---

次は`LottieItem.tsx`を呼び出す側の`Animation.tsx`を作成します。
完成コードは長いので、一部抜粋して掲載します。
完成形は[Github](https://github.com/yamanayama/QiitanApp)をご確認ください。

```React:Animation.tsx
/** @jsxImportSource @emotion/react */
import { css, keyframes } from "@emotion/react";
import React, { useState } from "react";
//Lottie用コンポーネント
import { LottieItem } from "./LottieItem";
//アニメーションデータ
import angry from "./animationData/angry.json";
import smile from "./animationData/smile.json";
import jump from "./animationData/jump.json";
//背景画像
import BackgroundRed from "./images/BackgroundRed.png";
import BackgroundAngry from "./images/BackgroundAngry.jpg";

//stateの型定義をします
type Action = "jump" | "smile" | "angry";

export const Animation: VFC = () => {
  //初期値はjumpです
  const [action, setAction] = useState<Action>("jump");
  const isJump = action === "jump";
  const isSmile = action === "smile";
  const isAngry = action === "angry";

  return (
    <div css={[backgroundImage(action === "angry")]}>
      <div css={container}>
        <div css={serif}>Qiitanに話しかけよう！</div>
        <div css={stage}>
          {isJump && <LottieItem animation={jump} loop={true} />}
          {isSmile && <LottieItem animation={smile} loop={false} />}
          {isAngry && <LottieItem animation={angry} loop={false} />}
        </div>

        <button onClick={() => setAction("jump")} css={button}>
          Qiitan、ジャンプして！
        </button>
        <button onClick={() => setAction("smile")} css={button}>
          Qiitan、笑って！
        </button>
        <button onClick={() => setAction("angry")} css={button}>
          Qiitan、怒った…？
        </button>
      </div>
    </div>
  );
};

const backgroundImage = (isAngry: boolean) => css`
  ${isAngry
    ? `
    background: url(${BackgroundAngry}) no-repeat top center/100%;
  ` : `
    background: url(${BackgroundRed}) repeat-x center center/100vw;
  `}
  animation: ${isAngry || scroll} 60s linear infinite;
  height: 812px;
  margin: auto;
  position: relative;
  width: 375px;
`;
```

ピックアップして解説していきます。

まずLottieと、After Effectsで書き出したjsonをimportします。

```React:Animation.js
import Lottie from 'react-lottie';
import angry from "./animationData/angry.json"
import smile from "./animationData/smile.json"
import jump from "./animationData/jump.json"
```

### :qiitan-cry:アニメーションの切り替え

今回は`useState`を使用し、各アニメーションと連動したボタンが押された時だけ表示されるようにしています。

```ts:
//stateの型定義をします
type Action = "jump" | "smile" | "angry";

export const Animation: VFC = () => {
  //各アニメーションのクリックイベントをuseStateで管理します
  //初期値はjumpです
  const [action, setAction] = useState<Action>("jump");
  const isJump = action === "jump";
  const isSmile = action === "smile";
  const isAngry = action === "angry";
 
  //「Qiitan、ジャンプして！」を押した場合のみ、jumpのアニメーションがループで再生される
  {isJump && (<LottieItem animation={jump} loop={true} />)}
```

JSX側でLottie用コンポーネントの`LottieItem.tsx`を呼び出し、`props`として`animationData`、`loop`の`true/false`を渡してあげます。


### :qiitan:Style

最後に、スタイル側（Emotion）へも状態変化を渡し、再生されるアニメーションごとに背景画像を入れ替えましょう。

```ts:style（抜粋）
// 怒ったアニメーションの場合のみ、背景画像を差し替え、アニメーションをストップします
const backgroundImage = (isAngry: boolean) => css`
  ${isAngry
    ? `
    background: url(${BackgroundAngry}) no-repeat top center/100%;
  ` : `
    background: url(${BackgroundRed}) repeat-x center center/100vw;
  `}
…
```

### :qiitan-cry:これで完成！
宇宙一かわいいQiitanが生まれましたね！
<img width="375" alt="fix.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321220/c14132dd-3316-41fe-373b-c3f4132b05f7.gif">


## :qiitan:最後に
いかがでしたか？
皆さんも宇宙一かわいいQiitanを作って、巨大Qiitanぬいぐるみを手にいれましょう♫

:xmas-wreath2::qiitan:キャンペーンページはこちら:qiitan::xmas-wreath2:

https://qiita.com/advent-calendar/2021/campaign

[Ateam Brides Inc. Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ateam-brides)の12日目は、
@hyshrがお送りします！！要チェックや！！

### :qiitan-cry:素材
[OKUMONO様：背景画像をお借りしました。](https://sozaino.site/)

### :qiitan:スペシャルサンクス
本記事の作成にあたり、下記の方々にご協力いただきました。皆様ありがとうございました！！🎉
- [Keisuke Watanukiさん](https://qiita.com/xrxoxcxox)
- [ttkさん](https://qiita.com/ttk)