最近、本業で動画制作を行う機会があり、Adobeでモーションを作成したりしていました。
そこで作成したモーションをWEBでも再生できないかと思いやってみました。

今回はlottieというライブラリを用いて行います。
サイト本体はGatsbyを使用しましたが、React環境であれば同様の方法で試せると思います。

lottieとは


Airbnbが開発したライブラリ。AfterEffectsで作成したモーションをAndroid,ReactNativeなどで再生できるようにしてくれます。。
WEBではlottie-webというライブラリを使用します。
lottie
lottie-web

動画制作

AfterEffectsを用いて行いました。
bodymovinというライブラリを使用することで、AfterEffectsで作成したモーションをJSON形式で書き出すことができます。
詳しくは解説しませんが、以下の記事が参考になりました。
AfterEffects+Lottieを使ったアニメーションをWebサイトで表示させてみた

また、動画がなくてもlottiefileからいろんなクリエイターの方が作成したモーションをダウンロードすることもできます。
lottiefiles

環境構築


動画をGatsbyで取り込むまでを解説します。
環境構築をして作成した動画をファイルに取り込みましょう。

Gatsbyのプロジェクトを作ります。starterがあるので使うと便利です。

gatsby new gatsby-starter-default https://github.com/gatsbyjs/gatsby-starter-default


ちなみに私はTypeScriptで動かしてみたいなあと思います。
興味ある方は以下のプロジェクトをgit cloneしてください。
GitHub

publicファイルに専用のファイルを作成します。

cd public
mkdir animations


animationsに動画ファイルを入れておきましょう。

lottie-webをインストールします。

yarn add lottie-web


使用するフック

今回使用するメソッドの紹介です。Reactの基本です。

useEffect


関数コンポーネント内で副作用を処理するフックです。ユーザーの処理に応じて関数を返して処理を行うことができます。
今回はここにlottieを表示させるコードを書きます。コンポーネントの表示とともに処理を行うので今回は関数を返しません。

useEffect

useRef


refとはDOMにアクセスする関数のことです。
初期値をカッコに入れます。

const inputEl = useRef(null);


そして、

<input ref={inputEl} type="text" />

このようにDOMにアクセスして、処理を加えたりすることができます。

useRef

TypeScript対応


蛇足です。 lottie-webには型定義ファイルが(探した限り)なかったので以下のようなファイルを作成します。

//src/types/index.d.ts
declare namespace Lottie {
  export interface AnimationItem {
    play(): any;

    stop(): any;

    pause(): any;

    // one param speed (1 is normal speed)
    setSpeed(speed: number): any;

    // one param direction (1 is normal direction)
    setDirection(direction: number): any;

    // If false, it will respect the original AE fps. If true, it will update as much as possible. (true by default)
    setSubframe(flag: boolean): any;

    // first param is a numeric value. second param is a boolean that defines time or frames for first param
    goToAndPlay(value: number, isFrame: boolean): any;

    // first param is a numeric value. second param is a boolean that defines time or frames for first param
    goToAndStop(value: number, isFrame: boolean): any;

    // first param is a single array or multiple arrays of two values each(fromFrame,toFrame), second param is a boolean for forcing the new segment right away
    playSegments(
      segments: number[] | number[][],
      forceFlag: boolean,
    ): any;

    // To destroy and release resources.
    destroy(): any;
  }

  export interface AnimationConfig {
    // an Object with the exported animation data.
    animationData?: any;

    // the relative path to the animation object. (animationData and path are mutually exclusive)
    path?: string;

    // true / false / number
    loop?: boolean | number;

    // true / false it will start playing as soon as it is ready
    autoplay?: boolean;

    // animation name for future reference
    name?: string;

    // 'svg' / 'canvas' / 'html' to set the renderer
    renderer?: string;

    // the dom element on which to render the animation
    container?: any;
  }
}

declare class LottyPlayer {
  // optional parameter name to target a specific animation
  play(name?: string): any;

  // optional parameter name to target a specific animation
  stop(name?: string): any;

  // first param speed (1 is normal speed) with 1 optional parameter name to target a specific animation
  setSpeed(speed: number, name?: string): any;

  // first param direction (1 is normal direction.) with 1 optional parameter name to target a specific animation
  setDirection(direction: number, name?: string): any;

  // default 'high', set 'high','medium','low', or a number > 1 to improve player performance. In some animations as low as 2 won't show any difference.
  setQuality(quality: string | number): any;

  // param usually pass as location.href. Its useful when you experience mask issue in safari where your url does not have # symbol.
  setLocationHref(href: string): any;

  // returns an animation instance to control individually.
  loadAnimation(
    params: Lottie.AnimationConfig,
  ): Lottie.AnimationItem;

  // you can register an element directly with registerAnimation. It must have the "data-animation-path" attribute pointing at the data.json url
  registerAnimation(element: any, animationData?: any): any;

  // looks for elements with class "lottie"
  searchAnimations(
    animationData: any,
    standalone: boolean,
    renderer?: string,
  ): any;

  // To destroy and release resources. The DOM element will be emptied.
  destroy(name?: string): any;
}

declare const Lottie: LottyPlayer;

declare module 'lottie-web' {
  export = Lottie;
}


以下のissueを参考にさせていただきました。
anyだらけにしたので、あまり参考になりませんがとりあえず動きます。
GitHub

実装


前提知識を元にコードを書いていきましょう。
コードの全体像は以下のようになります。

// src/pages/index.tsx
import React, { useEffect, useRef } from 'react';
import lottie from 'lottie-web';

function IndexPage() {
  const animationContainer = useRef<HTMLDivElement>(null);

  useEffect(() => {
    lottie.loadAnimation({
      container: animationContainer.current,
      path: '/animations/data.json',
    });
  });

  return (
    <>
      <h1> Welcome!!</h1>
      <div ref={animationContainer} />
    </>
  );
}

export default IndexPage;



まずはuseRefの部分です。

 const animationContainer = useRef<HTMLDivElement>(null);


このようにuseRefを宣言して初期値nullを入れます。型はこのように書きます。今回はdiv要素にアクセスするのでHTMLDivElementを使っています。

   <div ref={animationContainer} />

そしてこのようにしてアクセスして、アニメーションを表示させます。

次にlottie-webの設定方法をみていきましょう。
loadAnimationというメソッドを使って、設定を書いていきます。先ほどのuseEffectの中で処理を行なっていきます。

  useEffect(() => {
    lottie.loadAnimation({
      container: animationContainer.current,
      path: '/animations/data.json',
    });
  });


containerにはアニメーションをレンダリングするDOM要素を設定します。
.currentプロパティは先ほどのrefの処理によって、DOMノードに設定されたプロパティです。DOMに変更があるたびに設定されます。これによって、loop再生することが可能になります。
path
先ほど入れた動画のパスを絶対パスで記述します。
他にもloop,autoplayなどが設定できます。

詳しくは以下のDocsを参照してください。
lottier-web

以上で以下のようなAnimationが表示されました



まとめ


lottieを使えば、簡単に複雑なアニメーショをWEBなどに取り込むことができます。
今回はGatsby特有の話はありませんでしたが、使い方を考えればJamstack構成などにすることによってパフォーマンスを維持したままアニメーションを行うことも可能です。

ただ、 AfterEffeects全ての機能が使えるわけではなくなかなか大変でした。
ドキュメントも参照しながら動きのある楽しいサイトを制作してみてください!