LoginSignup
22
11

More than 1 year has passed since last update.

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

Posted at

はじめに

簡単に優れたUIのコンポーネントを作成することができるということでコンポーネントライブラリを用いることがよくあります。そんなコンポーネントライブラリのうちChakra UIを導入するときに必要になったFramer Motionというライブラリについて解説します。

この記事はReactについての知識があれば読み進める内容となっています。Chakra UIは使用しません。

Framer Motion

Framer MotionはReact用のアニメーションのライブラリです。シンプルかつ強固に宣言的な記法ができます。宣言的な面がReactの思想とも合っていて使い心地が非常に良いライブラリとなっています。

上記の実装のために必要なコードはこれだけです。タップしたりホバーした時に設定したアニメーションが動作します。

<motion.div
  whileHover={{ scale: 1.2, rotate: 90 }}
  whileTap={{
    scale: 0.8,
    rotate: -90,
    borderRadius: "100%"
  }}
/>

利用方法

パッケージは以下のようにインストールして利用します。

npm i framer-motion
pnpm add framer-motion

基本的な使い方

Framer Motionはモーションコンポーネントを主としたライブラリです。このコンポーネントはFramer Motionによるアニメーション機能が備わったHTML要素のように扱えます。
例えば<motion.div />は、<div>にFramer Motionによるアニメーション機能が備わったと考えて操作することが出来ます。

import { motion } from 'framer-motion';

const SampleMotion = (): JSX.Element => (
  <motion.div />
);

基本のアニメーション

Framer Motionによるアニメーション機能はanimateに値を設定して付与することが出来ます。
例えばanimateに以下のように設定されているとします

<motion.div animate={{ x: 0 }} />

その後、何らかのきっかけで以下のようになった場合はこのコンポーネントが横軸に100px移動するアニメーションが画面に表示されます。

<motion.div animate={{ x: 100 }} />

画面収録_2023-01-31_18_01_42_AdobeExpress.gif
アニメーションの振る舞いはtransitionで細かく定義することが出来ます。
例えば単振動のように振る舞うをするアニメーションは以下のように作れます。

<motion.div animate={{ x: 100 }} transition={{ type: 'spring', damping: 0 }} />

画面収録_2023-01-31_18_37_24_AdobeExpress.gif
type: 'spring'でバネのように動くように指定して、damping: 0で減衰を0に設定しています。
設定できる他の値についてはこちらを参照してください。

ジェスチャー

ホバーやタップ、ドラッグなどのユーザーの動作に合わせたアニメーションを設定することが出来ます。
ホバーはwhileHoverで、タップはwhileTapのようにwhile+動作に値を設定してモーションコンポーネントにその動作に合わせたアニメーションを付与します。
ホバーした時にサイズを4倍にするようなアニメーションは以下のように書きます(サイズのスケールは一次元の倍率なので2に設定します)。

<motion.div whileHover={{ scale: 2 }} />

画面収録_2023-01-31_19_09_14_AdobeExpress (1).gif
whileHoverなどに渡せる設定値はanimateに渡せる値と同じです。

スクロール

スクロールに関するアニメーションはスクロールに連動したものと画面(ビューポート)に要素が現れると発生するものの二種類があります。
スクロールに連動したアニメーションはuseScrollを用いて作成します。useScrollはピクセル単位のスクロールの絶対的な位置を表すscrollXscrollYと0~1で定義されたスクロール位置を返すscrollXProgressscrollYProgressの4つの値をオブジェクトとして返します。

const { scrollYProgress } = useScroll();

return (
  <motion.div style={{ scaleX: scrollYProgress }} />
);

これらの返り値はReactで管理された状態ではないので、変化しても再レンダリングは起きません。そのためuseEffectuseMemoなどの第二引数の一部として渡したとしても想定通りの動作をしません。そのため、変化した時に何らかの処理を実行したり変化を監視したい場合はuseMotionValueEventを用いる必要が有ります。例えば、縦軸のスクロールの進捗が変わるたびに実行したい場合は以下のように書けます。

const { scrollYProgress } = useScroll();

useMotionValueEvent(scrollYProgress, "change", (latest) => {
  console.log(latest);
});

また、useScrollの返り値はuseSpringというhooksを用いてバネのような増加方法を実現することもできます。

const { scrollYProgress } = useScroll();
const scaleX = useSpring(scrollYProgress);

return <motion.div style={{ scaleX }} />;

続いてスクロールによって起きるアニメーションです。このアニメーションはwhereInViewを用いて行います。初期条件が必要だったらinitialを用いて設定します。

<motion.div
  initial={{ opacity: 0 }}
  whereInView={{ opacity: 1 }}
/>

ジェスチャーに似ていますが、スクロールという動作と考えると妥当ですね。このアニメーションはviewportで詳細を設定することが出来ます。基本的にはモーションコンポーネントが画面内に入った時に指定したアニメーションが動作しますが、指定したコンポーネント内に現れた時にアニメーションを動かすためにはviewport内のrootを設定する必要があります。

const scrollRef = useRef(null)
  
return (
  <div ref={scrollRef} style={{ overflow: "scroll" }}>
    <motion.div
      initial={{ opacity: 0 }}
      whileInView={{ opacity: 1 }}
      viewport={{ root: scrollRef }}
    />
  </div>
);

さいごに

Framer Motionについて、基本的な使い方を紹介しました。これまでCSSを用いたアニメーションは苦手としていましたが、このライブラリを用いて作成するのは楽しくできそうだなという印象を持ちました。
このライブラリはかなり豊富な機能があり全てを紹介しきれませんでしたが、他の機能もとても使いやすいのでドキュメントなどを参照しつつ利用してみてはいかがでしょうか。

22
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
11