[React アニメーション]簡単なサイドバーのアニメーションからFramer Motionに入門してみる。


サイドバーの開閉のアニメーションにframer-motionを使ったので、記事にしてみます。

サイドバーのアニメーション程度だったら他のライブラリを使うのもありかもしれません。
ただ、ある程度高機能なものを使えるようになっておいた方が、リッチなUIのアプリに対応できるので今回はframer-motionを選びました。Framer Motionは

Framer Motion

npm trendsをみても順当にシェアを伸ばしているので、これからも使われていきそうですね。
https://www.npmtrends.com/framer-motion-vs-motion-vs-react-motion-vs-react-spring-vs-react-transition-vs-react-transition-group

アニメーションをつける前のサイドバー

とりあえず、アニメーションをつける前のコードを載せておきます。

const SideMenuArea: React.FC<Props> = ({ setIsOpenSideMenu, isOpenSideMenu }) => {
  const sideMenuWidth = 336;

  return (
    <>
      {isOpenSideMenu && (<aside
        className="h-screen"
        css={css`
          min-width: ${isOpenSideMenu ? sideMenuWidth : 0}px;
        `}
       >
        サイドバーに表示したい内容。ナビゲーションとか、グラフとか。
      </aside>)
    <>
  )
}

サイドメニューにアニメーションをつける。

次にインストールと、動くコードです。

$ yarn add framer-motion
const SideMenuArea: React.FC<Props> = ({ setIsOpenSideMenu, isOpenSideMenu }) => {
  const sideMenuWidth = 336;

  return (
    <>
      <AnimatePresence>
        {isOpenSideMenu && (
          <motion.aside
            className="h-screen"
            css={css`
              min-width: ${isOpenSideMenu ? sideMenuWidth : 0}px;
            `}
            initial={{ opacity: 0 }}
            animate={{ opacity: 1, x: 0, minWidth: isOpenSideMenu ? sideMenuWidth + 'px' : 0 }}
            exit={{
              opacity: 0,
              x: 0,
              width: 0,
              minWidth: 0,
              transition: { opacity: { duration: 0.3 }, duration: 0.8 },
            }}
            transition={{ duration: 0.8 }}
          >
            サイドバーに表示したい内容。ナビゲーションとか、グラフとか。
          </motion.aside>
        )}
      </AnimatePresence>
    <>
  )
}

ここでは、framer-motionのAnimatePresenceとmotion.aside(motion.~~)を使っています。

AnimatePresence

AnimatePresenceをつけることで、コンポーネントを削除したときにもアニメーションが効くようになります。

そのため、上記のコードからAnimatePresenceを削除すると、サイドバーを開く場合にはアニメーションするのですが、閉じるときにアニメーションしないようになります。

今回はコンポーネントを削除するので必要ですが、別のシチュエーションで、コンポーネントを削除せずにアニメーションをつけたい場合は不要になります。

AnimatePresence

motion.~~

framer-motionでは、motion.の後にhtmlタグをつけることで、animationの対象とすることができます。例えば、motion.divのようにも書くことができ、今回はasideタグをanimationさせたいので、motion.asideとしています。

このタグのプロパティにアニメーションさせたい内容を書きます。アニメーションさせたい内容がanimateプロパティに書かれることが多そうです。今回はサイドメニューの開閉時にアニメーションをつけたいので、initialとexitを追加して、animateとの差分で変化していきます。

initialとanimationとexitがあれば一応動くのですが、アニメーションの時間も調整したいですね。transitionを書けば簡単に指定できます。さらに、transitionを複数箇所に設定することで、細かく調整することもできます。

<motion.aside
  // ~~
  exit={{
  opacity: 0,
  x: 0,
  width: 0,
  minWidth: 0,
  transition: { opacity: { duration: 0.3 }, duration: 0.8 }}}
  transition={{ duration: 1 }}
>

上記のコードではmotion.aside直下のtransitionで、まず全体のtransitionを設定しています。
さらに、exitの中で、exit全体のdurationとopacityのみのdurationを切り替えています。
これで、サイドメニューが消える前に、先にコンテンツが消えるようにできます。