Framer Motionを使ってReactでアニメーションを作成する

Framer Motionは、Reactアプリケーションにアニメーションとジェスチャーを簡単に追加するためのライブラリです。提供されたファイルでは、特にReact Routerを使用したページ遷移アニメーションの実装に活用されています。

framer motionをインストールする。Reactのバージョンが18の場合

npm i framer-motion

Reactのバージョンが19の場合

npm install motion

ファイルの構成

index.html
src/
├── About.jsx
├── App.css
├── App.jsx
├── main.jsx
├── motionVariants.js
├── MotionWrapper.jsx
├── Top.jsx

├── js/
│   └── main.js
└── img/
    ├── typescript.svg
    └── vite.svg
  • About.jsx アバウトページ
  • App.css 各ページに適用されるスタイルシート
  • App.jsx UIを構成するコンポーネント
  • main.jsx Reactアプリケーションがブラウザで実行される際のエントリポイントとなるファイル
  • motionVariants.js Framer Motionを使用して定義されたアニメーションの定義セット
  • MotionWrapper.jsx Framer Motionの機能を再利用可能なラッパーコンポーネントとして提供する
  • Top.jsx トップページ

MotionWrapper.jsxの説明

import { useLocation } from "react-router-dom";
import { motion } from "motion/react";
import { fadeVariants } from './motionVariants';

export const MotionWrapper = ({ children, delay = 0, duration = 1, ...rest }) => {

  const location = useLocation();

  return (
    <>
      <motion.div 
        variants={fadeVariants} // バリアントのセットを渡す
        initial="hidden" // 初期状態とアニメーション後の状態をバリアント名で指定
        animate="visible" // 初期状態とアニメーション後の状態をバリアント名で指定
        exit="exit" // AnimatePresenceで使用するためにexitも設定
        transition={{// propsとして受け取ったdelayをtransitionにマージ
          delay: delay,
          duration: duration
        }}
        onAnimationStart={() => {
          console.log("onAnimationStart", location.pathname)
        }}
        onAnimationComplete={() => {
          console.log("onAnimationComplete", location.pathname)
        }}
        {...rest} // その他のprops(className, styleなど)を継承
      >
        {children}
      </motion.div>
    </>
  );
};

export default MotionWrapper;

motionをインポートしてアニメーションさせる要素のタグにmotion.を追加する。variantsは、アニメーションの複数の状態を定義したものでhidden、visible、exitが定義されている。initialはアニメーション開始前の初期状態でanimateはアニメーション完了後の状態になる。exitはコンポーネントがReactのDOMツリーからアンマウントされるときの状態を定義する。

motionVariants.jsの説明

export const fadeVariants = {

  hidden: {// 初期状態(initial): 遷移前の状態や、非表示の状態
    opacity: 0
  },
  visible: {// アニメーション後の状態(animate): 表示された状態
    opacity: 1,
  },
  exit: {// 退出状態(exit): AnimatePresenceで使用する退出アニメーション
    opacity: 0,
  }
};

export default fadeVariants;
定義名対応するFramer Motionのプロパティ意味アニメーションのスタイル
hiddeninitialコンポーネントの初期状態または非表示状態。opacity: 0
(完全に透明)
visibleanimateアニメーションが完了し、コンポーネントが完全に表示された状態。opacity: 1
(完全に不透明)
exitexitAnimatePresenceによってコンポーネントがDOMから削除される時の退出アニメーションの状態。opacity: 0
(完全に透明)

退出アニメーションの実行 (Exit Animation)について

import { Link, Routes, Route, useLocation } from 'react-router-dom';
import Top from './Top';
import About from './About';
import { AnimatePresence } from 'motion/react';

const App = () => {

  const location = useLocation();

  return (
    <>
      <header className={`headerNav--01 headerNav_close headerNav_closeEnd`}>
        <div className={`gutter--01`}>
          <div className={`headerNav-header`}>
            <div className={`headerNav-inner`}>
              <Link to={`/`}>home</Link><br />
              <Link to={`/about/`}>about</Link><br />
            </div>
          </div>
        </div>
      </header>
      <main>
        <div className={`main--02`}>
          <div className={`gutter--01`}>
            <div className={`wrap--01`}>
              <div className={`main-contents`}>
                <AnimatePresence mode="wait">
                  <Routes location={location} key={location.pathname}>
                    <Route path="/about/" element={<About location={location} />} />
                    <Route path="/" element={<Top location={location} />} />
                    <Route path="*" element={<Top fallbackRoute={true} />} />
                  </Routes>
                </AnimatePresence>
              </div>
            </div>
          </div>
        </div>
      </main>
      <footer className={`footer--01`}></footer>
    </>
  );
}

export default App;

AnimatePresenceでラップされた子コンポーネント(この例ではを使用しているページコンポーネント)がアンマウントされそうになると、AnimatePresenceはそのコンポーネントを一時的にDOMに保持し続けます。

保持されている間に、子コンポーネントに設定されたexitプロパティ(例: exit=”exit”)のアニメーションが実行され、アニメーションが完了した時点で、コンポーネントはDOMから安全に削除されます。

App.jsxでは、AnimatePresenceがReact Routerのコンポーネントをラップすることで、ページ遷移アニメーションの中心的な役割を果たしています。

プロパティ役割
keyプロパティ<Routes>(またはその子要素)にkey={location.pathname}を設定することで、URLが変わるたびにコンポーネントが置き換わったと認識させ、AnimatePresenceに古いコンポーネントの削除を通知します。
mode=”wait”遷移のシーケンスを制御する最も重要な設定です。 退出するページ(古いページ)のexitアニメーションが完全に終わるまで、入場するページ(新しいページ)のinitialanimateアニメーションの開始を待機させます。これにより、ページ同士が重なることなく、スムーズに切り替わります。

GitHubにファイルをアップする。