Gatsbyウェブサイトにアニメーションを追加する[Framer-Motion]

Gatsbyウェブサイトの見栄えをよくするために、アニメーションを使えるようにしてみましょう。

やっぱりウェブサイト上に動くものがあると印象的になりますからね。…とはいえ、ウェブサイトが読みにくくなったりもするので、使いすぎには注意したいところですね。

個人的には、最初に表示するときだけアニメーションが動くようにして、それ以降は動かない…というのがちょうど良い感じです。

framer-motionのインストール

今回は、Gatsbyウェブサイトのプロジェクトが既にある前提で、framer-motionというライブラリを利用して、アニメーションを作成していきます。

まずはframer-motionをインストールするところから始めます。以下のコマンドを利用してframer-motionを使えるようにします。

npm install framer-motion

これだけで準備は完了です。
次に、実際のページでframer-motionを利用していきます。

framer-motionの基本的な使い方

まずはいつも通りページを作っていきます。

サンプルとして、src/pagesフォルダ内にmotion.jsファイルを作成して、以下のサンプルのコードを追加します。

まずはインストールしたframer-motionをインポートします。

import { motion } from "framer-motion"

最低限の準備はこれだけで完了です。

あとはアニメーションで動かしたい要素の前に、motionを追加して、どのように動かしたいかを設定するだけです。

例えば、<div>を動かしたいなら、<motion.div>、<button>を動かしたいなら、<motion.button>という具合です。

まずはボタンを試しに動かしてみましょう。

<motion.button
   animate={{ rotate: 360, scale:[null,2,3,1], x:[null,100,-200,0], y:[null,-200,100,0]}}
   transition={{ duration: 4, times: [0,0.5,0.8,1] }}
>
   ボタンを押してね!
</motion.button>

無駄に複雑に動かしてみました。


画像1

framer-motionで要素を動かしたい場合

animateにxとyの値を設定して動かすことができます。

<motion.button
    animate={{ x:100, y:100}}
>
    動くボタン
</motion.button>

右に動かすには、xを正の値に設定し、左に動かす場合は負の値にします。
上に動かすには、yを負の値に設定し、下に動かすにはyを正の値にします。

ただこの設定だと、一瞬で移動するのでアニメーションしているようには見えませんね。

そんな時は、transitionでdurationを指定することで、移動にかける時間(秒数)を設定することができます。

<motion.button
    animate={{ x:100, y:100}}
    transition={{ duration: 2 }}
>
    動くボタン
</motion.button>

画像2

framer-motionで要素を回転させる場合

animateでrotateを設定します。数字は何度、回転させるかを指定できます。

<motion.button
   animate={{ rotate: 90 }}
   transition={{ duration: 2 }}
>
   90度
</motion.button>

<motion.button
   animate={{ rotate: 180 }}
   transition={{ duration: 2 }}
>
   180度
</motion.button>

<motion.button
   animate={{ rotate: 270 }}
   transition={{ duration: 2 }}
>
   270度
</motion.button>

<motion.button
   animate={{ rotate: 360 }}
   transition={{ duration: 2 }}
>
   360度
</motion.button>

画像3

framer-motionで要素を表示させる場合

最初は何も表示されていなくて、徐々に要素を表示させたいこともあります。そんな場合は、animateにopacityを設定します。

0は完全に見えない状態、1は完全に見えている状態で、0〜1の値を設定できます。

特に何も設定していない場合、opacityは1なので、これをanimateでopacity:0にすることで消えるボタンを設置できます。

<motion.button
    animate={{ opacity: 0 }}
    transition={{ duration: 2 }}
>
    消えるボタン
</motion.button>

とはいえ、これでは見えない状態から表示させることができませんよね。もちろんcssでopacityを0に設定して、animateでopacityを1にしても良いでしょう。

もう1つの方法として、motionでは初期状態も設定することもできます。

initialにopacity:0とし、animateでopacity:1にすることで、表示するボタンを作ることができます。

<motion.button
    animate={{ opacity: 1 }}
    initial={{ opacity: 0 }}
    transition={{ duration: 2 }}
>
    浮き出るボタン
</motion.button>

画像4

framer-motionで無限にアニメーションさせる場合

何かを半永久的に動かしたいこともあるかもしれません。そんな時は、transitionにrepeat回数をつけることができます。

この時、repeat回数にInfinityを設定すれば無限にアニメーションさせることができます。

<motion.button
   animate={{ opacity: 1 }}
   initial={{ opacity: 0.5 }}
   transition={{ repeat:Infinity, duration: 2 }}
>
   点滅するボタン
</motion.button>

ただ、この書き方だと、opacityを0.5→1を繰り返すようになっています。そのため、ループする時に、opactiy:1→0.5に変わるのが一瞬で行われてしまいます。

これを避けるために、repeatTypeに"mirror"を設定します。

<motion.button
   animate={{ opacity: 1 }}
   initial={{ opacity: 0.5 }}
   transition={{ repeat:Infinity, repeatType:"mirror" ,duration: 2 }}
>
   点滅するボタン
</motion.button>
           

画像5

framer-motionで要素が表示された後に動かす

次に気になるところとしては、これらのアニメーションはページがロードされた時から、自動的に動き始めています。

そのため、ページの下の方の要素でアニメーションを行っていると、それをユーザーが見る前にアニメーションが終わっているなんてことが起こりかねません。

これを避けるために、要素が表示された後に動かすようにしてみましょう。これを行うには少し複雑なので、別途コンポーネントを作成します。

src/componentsフォルダにanimate_in_view.jsを作成します。

# src/components/animate_in_view.js

import * as React from 'react'
import { motion, useAnimation} from "framer-motion"
import { useEffect } from "react"
import { useInView } from "react-intersection-observer"

function AnimateInView({ children }) {

   const controls = useAnimation();
   const [ref, inView] = useInView();
 
   const variants = {
     moved: { x:100 },
     initial: { x:0 }
   }
   useEffect(() => {
     if (inView) {
       controls.start("moved");
     } else {
       controls.stop();
       controls.set("initial");
     }
   }, [controls, inView]);
 
   return (
     <motion.div
       ref={ref}
       animate={controls}
       initial="initial"
       transition={{ duration: 3 }}
       variants={variants}
     >
       {children}
     </motion.div>
   )
 }

export default AnimateInView

variantには変形という意味があります。ここにはアニメーションの結果の状態を定義しておきます。"initial"に初期状態、"moved"にアニメーション後の状態を定義しました。

framer-motionのuseAnimationのcontrolsでは、start()でアニメーションを開始し、stop()でアニメーションを止めることができます。また、set()でアニメーションせずに直接その定義に変更することができます。

上記では、要素が見える状態になったとき、"moved"へのアニメーションを開始します。要素が見えなくなったら、アニメーションを止めて、"initial"の初期状態に戻しました。

これによって、この要素がブラウザ上で表示される度に、移動が最初から行われるようにできました。

画像6

実装結果

まとめ

framer-motionを使って簡単なアニメーションを実装する方法を説明しました。これ以外にも様々な使い方ができるので、興味がある方は、公式のドキュメンテーションを読んでみてください。

ちなみに裏にある文章は青空文庫の「人間失格」をお借りしました。


ここまで読んでいただけたなら、”スキ”ボタンを押していただけると励みになります!(*´ー`*)ワクワク



この記事が気に入ったらサポートをしてみませんか?