React

Framer Motion のバンドルサイズを軽量化してパフォーマンスを上げる

Framer Motion で LazyMotion を使ってパフォーマンスを上げる方法を紹介します。

Framer Motion のバンドルサイズを軽量化してパフォーマンスを上げる

コンニチハ!ハルミです。

React のライブラリである Framer Motion は、リッチなアニメーションを簡単に実装できる強力なライブラリです。

しかし、Framer Motion は比較的バンドルサイズが大きく、読み込みパフォーマンスに影響を与えることがあります。

これは、Framer Motion がアニメーションを実現するために、多くのコードをバンドルに含める必要があるためです。

この記事では、Framer Motion のバンドルサイズを軽量化してパフォーマンスを上げる方法を紹介します。

はじめに

当記事は React を使用します。
Reactの知識があることを前提としておりますのでご了承ください。

Framer Motion とは

念のためにFramer Motion が何なのか簡単に説明します。

Framer Motion は、Reactアプリケーション向けのオープンソースのアニメーションライブラリで、シンプルかつ強力な宣言的な記法を提供します。
このライブラリは、UIに動きを加えるためのツールとして広く利用されており、特にモダンなWeb開発において重要な役割を果たしています。

読み込み中...

近いものに有名な GSAP (GreenSock Animation Platform) がありますが、
Framer Motion は GSAP よりもシンプルで直感的に使えるという特徴があります。

バンドルサイズを軽量化する方法

早速本題に入っていきましょう。

通常のFramer Motionでは、以下のように要素にmotionを先頭につけてアニメーションを設定します。

sample.tsx
import { motion } from "framer-motion";

export const Sample = () => {
  return <motion.div
    whileHover={{ scale: 1.2, rotate: 90 }}
    whileTap={{
      scale: 0.8,
      rotate: -90,
      borderRadius: "100%"
    }}
    >Hello World</motion.div>;
};

ホバーで拡大、クリックで縮小できるよ!

しかし、このようにすると使用しない関数までバンドルされてしまいます。

これを防ぐために mLazyMotion を使用します。

mmotion と全く同じように使用しますが、
このmコンポーネントにはアニメーション、レイアウト アニメーション、ドラッグ ジェスチャなどの機能がプリロードされていません。

このmに対して、LazyMotionを使用して、手動で機能をロードします。

現在、ロードできる機能パッケージは2つあります。

  • domAnimation
  • domMax

domAnimationはアニメーション、バリアント、終了アニメーション、タップ/ホバー/フォーカス ジェスチャのサポートを提供します。

domMaxは上記のすべてに加え、パン/ドラッグ ジェスチャとレイアウト アニメーションもサポートします。

つまり、通常のDOMアニメーションを使用する場合はdomAnimationのみをロードすることで バンドルサイズを軽量化することができます。

sample.tsx
import { m, LazyMotion, domAnimation } from "framer-motion";

export const Sample = () => {
  return (
    <LazyMotion features={domAnimation}>
      <m.div
        whileHover={{ scale: 1.2, rotate: 90 }}
        whileTap={{
          scale: 0.8,
          rotate: -90,
          borderRadius: "100%"
            }}
      >
        Hello World
      </m.div>
    </LazyMotion>
  );
};

これでバンドルサイズを軽量化することができます。

Next.jsで使う場合はLazyMotionlayout.tsxで使用しておくと
ルート配下のページではmを使うだけでよくなるのでシンプルになります。

app/layout.tsx
import { LazyMotion, domAnimation } from "framer-motion";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body>
        <LazyMotion features={domAnimation}>
          {children}
        </LazyMotion>
      </body>
    </html>
  );
}

当サイトにもFramer Motionを使用していますが、
mを使用したところPageSpeed Insightsでのスコアが3点ほど上がりました。

motionのバンドルサイズはかなり大きめなので、基本的にはmを使用した方がよいでしょう。

厳格モード

LazyMotionstrictを指定すると、motionを使用した場合にエラーを吐きます。

これによって、誤ってmotionをインポートしてしまうことを防ぐことができます。

bad.tsx
function App() {
  // これはエラーになります🔥
  return (
    <LazyMotion strict>
      <motion.div />
    </LazyMotion>
  )
}

遅延読み込みについて

公式ドキュメントによると、WebpackRollup などのバンドラーを使用している場合は、初期レンダリングを実行した後にのみ機能を取得する動的インポート関数を渡すことができるようです。

こちらの機能は当方の環境では試せていないので、試したい方は公式ドキュメントを参照してください。

動的インポートは、こちらの機能を使わなくても各フレームワークの機能を使えば実現できるので、 そちらを使った方がよいかもしれません。

読み込み中...

おわりに

Framer Motion のバンドルサイズを軽量化してパフォーマンスを上げる方法を紹介しました。

これで、Framer Motion を使ってよりスムーズなアニメーションを実現できるようになるでしょう。

これに限らず、JSライブラリはバンドルサイズが大きいものが多いので、
パフォーマンスチューニングの際には注意しましょう。

それでは、また👋

profile

ハルミ

1997年生まれ。某メーカーの新米DX担当。
三度の飯より効率化が好き。
プログラミングにハマり、Webエンジニアを目指す。
現在React/Next.jsを学習しています🚀