2023.07.11

LottieFilesのアニメーションがNext.jsで簡単に実現できたので紹介します

LottieFilesとは?

Lottieは、小型、高品質、スクリプト化可能、インタラクティブで、実行時に操作できるオープンソースのアニメーションファイル形式です。

引用元:https://lottiefiles.com/

このLottieファイルを共有するプラットフォームがLottieFilesです。
自分では絶対に作れないようなアニメーションがたくさん…!

LottieFilesのキャプチャ
「loading」の検索結果

これほんとに無料でつかっていいの?と疑いたくなるようなものばかりです🫣💛
💁🏻‍♀️ライセンスはこちら https://lottiefiles.com/page/license

Next.jsでの実装手順

事前準備

LottieFilesで好きなアニメーションをJSON形式でダウンロードしておきます。

Next.jsプロジェクトのpublicディレクトリ配下に置けばOKです✌🏻

パッケージをインストール

lottie-reactというパッケージを利用しました。

https://www.npmjs.com/package/lottie-react

react-lottieというパッケージもあったのですが(紛らわしすぎ)、使い方がクラスコンポーネントで書かれていたのでやめました🥲

あとはlottie-reactは超わかりやすい解説サイトがあったのも決め手でした。
💁🏻‍♀️こちらです https://lottiereact.com/

正直、このサイト見て進めれば誰でも簡単に実装できちゃうと思います🙆🏻‍♀️

では本題に入って、まずはパッケージをインストール。

npm i lottie-react

Next.jsで呼び出す

'use client';

import Lottie from "lottie-react";
import animationData from "@/public/lottie-animation.json";

export const LottieAnimation = () => {
  return <Lottie animationData={animationData} />;
};

'use client'を宣言する必要があるので、コンポーネントに切り分けます。
多分内部でuseStateを使っているっぽい?そんなエラーがでます。

基本的な使い方はこれだけです!超簡単💯

応用編

実際にこのブログで使っている紙飛行機飛ばす部分を参考に、応用編として解説してみます。

記事詳細の最下部にあるこの部分

仕様紹介

  • 初期表示はアニメーションの途中部分
  • クリックをトリガーにアニメーションの一部をループさせる
  • 処理が完了したらアニメーションの最後の部分を実行

完成コード(一部省略あり)

'use client';

import Lottie from 'lottie-react';
import animationData from '@/public/lottie/lottie-astronaut.json';

export const LottieAnimation = () => {
  const [loop, setLoop] = useState<boolean>(false); // アニメーションのループを定義
  const [checked, setChecked] = useState<boolean>(false); // 紙飛行機がわたしの元にとどいたかどうか

  const options = {
    animationData,
    loop,
    autoplay: false,
    lottieRef,
  }
  
  // ループの状態を検知してアニメーションを実行
    useEffect(() => {
    if (!lottieRef.current) {
      return;
    }
    if (loop) {
      lottieRef.current.playSegments([42, 96], true);
    } else if (checked) {
      lottieRef.current.playSegments([97, 120], true);
    }
  }, [loop, lottieRef, checked]);

  const handleClick = async () => {
        setLoop(true);
    try {
      await func();
            setChecked(true);
    } catch(error) {
      console.error(error)
    } finally {
      setLoop(false);
    }
  }
  return (
    <button onClick={handleClick}>
      <Lottie {...options} initialSegment={[41, 96]} onComplete={handleCompleteAnimation} />
    </div>
  );
};

ボタンの2度押し防止や、エラーハンドリングなどは今回割愛しました。

ポイント

loopの状態をstateで管理して動的に切り替える

const [loop, setLoop] = useState<boolean>(false);

const options = {
  animationData,
  loop, // ココ!
  autoplay: false,
  lottieRef,
}

ループしたいところとしたくないところ、どうやって切り替えるか少し迷ったのだけどuseStateで問題なくできました✌🏻

ループの状態に応じてアニメーションを実行

useEffect(() => {
    if (!lottieRef.current) {
      return;
    }
    if (loop) {
      lottieRef.current.playSegments([42, 96], true);
    } else if (checked) {
      lottieRef.current.playSegments([97, 120], true);
    }
  }, [loop, lottieRef, checked]);

初期表示(ループなし)→ 処理中(ループあり)→ 処理完了(ループなし)
こんな感じにしたかったので、ループの状態を監視してアニメーションを操作しました。

playSegmentメソッドを使って指定した部分のアニメーションを実行することができます。
わたしの使ったアニメーションでは、42〜96フレームまでが空を飛んでいることろで、97〜120フレームまでが遠くへ飛んでいくところなのでこのようになっています。

初期表示の際にplaySegmentメソッドが実行されるのを防ぐため、else if (checked)で条件分岐をしています。

初期表示位置の調整とアニメーション終了時のPropsを追加

<Lottie {...options} initialSegment={[41, 96]} onComplete={handleCompleteAnimation} />

initialSegmentで初期表示のアニメーション位置を設定できます。
これはoptionsに含めると謎にエラーがでたので、Lottieコンポーネントで直接指定しました。

onCompleteでアニメーション終了時の動作も設定できます。
わたしはトーストを表示しました🚀

感想

アニメーションが高品質すぎてひとつ加えるだけでもサイトがリッチになった感じがします🥳

ちなみにこのサイトのトップページのアニメーションもlottie-reactを使っているのです。

ただちょっと重ためなのか表示に時間がかかることも、、
この辺は近々スケルトンで対応しようかなと思っています🐒

余談

最初、紙飛行機の部分はいいねボタンにしようと思っていて、アニメーションもハートのやつを想定していたのですが、
「記事を読んだ全員がいいね!って思うわけじゃないよな??」
「わたしはいいねって言ってほしいんじゃなくて、頑張って書いたね!エライヨ!がほしいんだよな???」
と思い紙飛行機にしました笑笑

なのでもし記事を読んでくれた方がいたら「ガンバッタネ」の意をこめて気軽に紙飛行機とばしてください☺️

記事のミスを指摘する場所がまだないので、そちらもそのうち実装したいなと思っております…!おわり!

最後まで読んでいただきありがとうございます!
もしよければ「読んだよ!」の代わりに↑の紙飛行機をクリックで飛ばしてください。わたしの元に届きます。

Special Thanks!!!

ありがとうございました