Framer Motionでいい感じのアニメーションをシンプルに実装しました [React / Next.js / App Router]
こんにちは。nakaatsuと申します。今回はポートフォリオサイトを作り直して個人ブログを作りました。
この記事では今回使用したアニメーションライブラリのFramer Motionについて紹介し、実際に実装したアニメーションについて解説します。Framer Motionとは
公式のドキュメントやexampleが多くあるので導入したいアニメーションの引き出しを考えながら実装しやすいです。
構成
今回は以下の構成でFramer Motionを実装します。
- Next.js / App Router
- Framer Motion
セットアップ
React環境のプロジェクトにframer-motion
パッケージを追加します。
yarn add framer-motion
アニメーション実装
まずは以下の要素にFramer Motionを追加してアニメーションを設定してみます。
import styles from './FramerMotionTest.module.css';
const FramerMotionTest = () => {
return (
<div className={styles.framerMotionContainer}>
<div className={styles.box}>
<p>うおお</p>
</div>
</div>
)
}
export default FramerMotionTest;
1. ふわっと表示
Framer Motionはimport { motion } from 'framer-motion'
でインポートし、<motion.要素名>
の形で取り扱うことができます。
まずは白いBox要素をふわっと表示させてみます。
'use client'
import { motion } from 'framer-motion';
import styles from './FramerMotionTest.module.css';
const FramerMotionTest = () => {
return (
<div className={styles.framerMotionContainer}>
<motion.div className={styles.box} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.4, delay: 0.3 }}>
<p>うおお</p>
</motion.div>
</div>
)
}
export default FramerMotionTest;
- initial: 要素が存在しない状態から現れるようにするため、不透明度を0に設定。
- animate: 不透明度を1にしてアニメーション後に要素が表示されるようにします。
- transition:
duration
でアニメーションにかかる時間を0.4秒に、delay
で0.3秒後にアニメーションが開始されるようにします。
これで<motion.div>
で囲ったBoxがふわっと表示されるようになりました。要素名にmotion.
を加えて値を設定するだけで既存コンポーネントにアニメーションを実装できるので扱いやすいです。
2. ぽよんと表示
ふわっと表示するアニメーションはCSSのみでも比較的実装しやすいものですが、Framer Motionではシンプルな記述でちょっといい感じのアニメーションを実装することができます。
次に右下からぽよんと飛び出してくるようなアニメーションを実装します。
const FramerMotionTest = () => {
return (
<div className={styles.framerMotionContainer}>
<motion.div className={styles.box} initial={{ x: 400, y: 200, scale: 0 }} animate={{ x: 0, y: 0, scale: 1 }} transition={{ duration: 0.6, delay: 0.6, type: 'spring', bounce: 0.3 }}>
<p>うおお</p>
</motion.div>
</div>
)
}
- initial: 表示位置を初期位置の0, 0から右下に配置し、大きさを
scale
で0に指定します。- animate: 表示位置を初期位置に戻し、大きさを1に指定し戻します。
- transition:
type: 'spring'
を指定するとぽよんと跳ねるアニメーションにできます。bounce
で剛性を設定でき、跳ねたときのぽよぽよ具合を調整できます。
transition
のtype
を指定するだけで、ライブラリが用意しているいい感じのアニメーションにすることができます。
このぽよんとしたアニメーションを、設定値を変えるとどう変化するのか試してみます。initial
のyをマイナス値にしてbounce
を0.8に上げてみます。
<motion.div className={styles.box}
initial={{ x: 0, y: -200, scale: 0 }}
animate={{ x: 0, y: 0, scale: 1 }}
transition={{ duration: 0.6, delay: 0.6, type: 'spring', bounce: 0.8 }}>
上から勢いよく跳ねて登場するようになりました。bounce
の値を上げることで前よりも強く引いて離したようなアニメーションになっているのが分かるかと思います。
3. 表示領域に入ったら動かす
ここまでanimate
でアニメーション後の値を指定していましたが、これだと要素が読み込まれたときから動き出してしまいます。
しかし、ファーストビューにない要素をアニメーションさせたいときは要素がユーザー側で実際に表示されたときに動かしたいです。その場合はanimate
をwhileInView
にすると表示されたら動くように指定できます。
const FramerMotionTest = () => {
return (
<>
<div className={styles.framerMotionContainer}>
<div className={styles.box}>
<p>うおお</p>
</div>
</div>
<div className={styles.framerMotionContainerTwo}>
<motion.div className={styles.box} initial={{ x: 48, y: 48, scale: 0 }} whileInView={{ x: 0, y: 0, scale: 1 }} transition={{ duration: 0.6, delay: 0.6, type: 'spring', bounce: 0.8 }}>
<p>うおお</p>
</motion.div>
</div>
</>
)
}
これで要素が視界に入ったら動くようになりました。このままだと要素が表示域から外れる->表示域に入るを繰り返すたびにアニメーションするので、一度だけ動かしたい場合はviewport={{ once: true }}
を追加します。
表示域とアニメーション開始位置をずらしたい場合は、viewport={{ margin: '120px' }}
のように指定することで実現できます。
4. スクロール量に合わせたアニメーション
whileInView
はスクロールされて要素が表示されたか否かで実行するアニメーションでしたが、どれくらいスクロールされたかによって要素を変形させることもできます。
Framer MotionではuseScroll()
を使うことでスクロールの割合を取得することができます。縦スクロールの割合をscrollYProgress
で取得してプログレスバーとして表示させてみます。
'use client'
import { motion, useScroll } from "framer-motion"
import styles from './ProgressBar.module.css'
const ProgressBar = () => {
const { scrollYProgress } = useScroll()
return (
<motion.div className={styles.progressBar} style={{ scaleX: scrollYProgress }} />
)
}
export default ProgressBar
.progressBar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 80px;
background: 'chartreuse';
transform-origin: 0%;
z-index: 999;
}
scrollYProgress: 縦スクロールの進み具合を取得できます。
scaleX
の値に割り当てることで横方向への変形を縦スクロールに連動させています。
スクロール量の取得をとてもシンプルに行うことができます。今回は単純に横サイズ変形に利用しましたが、Parallaxのような視差表示にも応用することができます。
公式サンプル:https://codesandbox.io/p/sandbox/framer-motion-parallax-i9gwuc?file=%2Fsrc%2FApp.tsx
5. ホバー・タップ
ボタン要素などユーザーにアクションさせる要素はマウスホバー時や画面タップ時にアニメーションをさせたいです。そんなときはwhileHover
whileTap
を設定するとそれぞれアニメーションさせることができます。
const FramerMotionTest = () => {
return (
<div className={styles.framerMotionContainer}>
<motion.button className={styles.box} whileHover={{ scale: 1.2, rotate: 360, backgroundColor: 'aquamarine', transition: { duration: 0.3 } }} whileTap={{ scale: 0.8, backgroundColor: 'beige' }}>
<p>うおお</p>
</motion.button>
</div>
)
}
- whileHover: ちょっと大きくなって1回転させました。
- whileTap: ちょっと小さくしてボタンを押したときに凹むイメージにしました。
PCとスマホでマウスホバーと画面タップのアニメーションを考える際に、短い記述でどちらも実装することができます。
おわりに
- ドキュメントやサンプルが充実していて学習コストが低め
-
motion.
を追加し設定値を入れるだけでアニメーションさせることができる- 既存コードを大きく変更せずに導入できる
- シンプルなアニメーションからスクロールを利用したものまで、今のウェブサイトで実装できるアニメーションが網羅されており様々なライブラリを入れる必要がない
- 動作が比較的軽い
など自分としてはなかなかおすすめできるアニメーションライブラリでした。Reactプロジェクトにアニメーションを導入したい・アニメーションの習熟をしたい方にはおすすめです。
今回は初めて導入したこともありシンプルなアニメーションを多く実装していますが、他にも要素をドラッグしたときのアニメーションやKeyframeでの制御など紹介しきれていない機能が数多くあります。
ぜひ他の機能も試して楽しいウェブサイトを作ってみてください。
Discussion