はじめに
Webアプリケーションにアニメーションを入れたいとき、どのライブラリを使うか迷いますよね。CSSアニメーションでゴリゴリ書くのも一つの手だけど、正直なところ、複雑なアニメーションになると管理が大変になってくる。
そこで今回紹介するのが Motion というライブラリ。以前は「framer-motion」という名前で知られていたやつが、リブランディングされてMotionになったんですよ。GitHubスター数30,000超えという、アニメーションライブラリとしてはかなりの人気を誇っている。
個人的には、ReactでちょっとしたUIアニメーションを実装するならMotion一択ですね。
Motionの特徴・メリット
シンプルなAPI
MotionはReact、JavaScript(バニラ)、Vueの3つの環境で使える。統一されたAPIデザインのおかげで、フレームワークを跨いでも学習コストが低いのがありがたい。
ハイブリッドエンジンによる高パフォーマンス
これ、意外と重要なポイントなんですよ。MotionはJavaScriptの柔軟性とネイティブブラウザAPIの性能を組み合わせた「ハイブリッドエンジン」を採用している。結果として、120fps対応のGPU加速アニメーションが実現できる。
スマホでヌルヌル動くアニメーションって、ユーザー体験的にかなり差が出るんだよな。
本番環境対応が充実
- TypeScript完全対応
- Tree-shaking対応で使わない機能はバンドルに含まれない
- 小さいファイルサイズ
- 広範なテストスイート
30代になって思うのは、「動けばいい」じゃなくて「本番で安心して使える」ことの重要性。その点、Motionはしっかりしている。
機能が豊富
標準で以下の機能が揃っている:
- ジェスチャー(ドラッグ、タップなど)
- スプリングアニメーション
- レイアウトアニメーション
- スクロール連動エフェクト
- タイムライン制御
別途プラグインを入れなくていいのは時短になる。
インストール方法
React / JavaScript
npm install motion
Vue
npm install motion-v
シンプルですね。余計な依存関係もなく、これだけでOK。
基本的な使い方
Reactでの使用例
import { motion } from "motion/react"
function FadeInBox() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
フェードインするボックス
</motion.div>
)
}
motion.divというコンポーネントを使って、initial(初期状態)とanimate(アニメーション後の状態)を指定するだけ。これでフェードインアニメーションが完成する。
ホバーアニメーション
import { motion } from "motion/react"
function HoverButton() {
return (
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
>
押してみて
</motion.button>
)
}
whileHoverとwhileTapでインタラクティブなアニメーションが簡単に実装できる。CSSで:hover疑似クラスを使うより直感的だと思う。
バニラJavaScriptでの使用例
import { animate } from "motion"
// 要素を右に100px移動
animate("#box", { x: 100 })
// 複数のプロパティをアニメーション
animate("#box", {
x: 100,
opacity: 0.5,
rotate: 45
}, {
duration: 0.8,
ease: "easeInOut"
})
Reactを使わないプロジェクトでも同じ感覚で使えるのは便利。
実践的なユースケース
モーダルのアニメーション
import { motion, AnimatePresence } from "motion/react"
import { useState } from "react"
function Modal() {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(true)}>開く</button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
className="modal"
>
<p>モーダルの中身</p>
<button onClick={() => setIsOpen(false)}>閉じる</button>
</motion.div>
)}
</AnimatePresence>
</>
)
}
AnimatePresenceを使うと、要素が消えるときのアニメーション(exit)も制御できる。これがないと、要素が一瞬で消えてしまって味気ないんですよね。
スクロール連動アニメーション
import { motion, useScroll, useTransform } from "motion/react"
function ParallaxSection() {
const { scrollYProgress } = useScroll()
const y = useTransform(scrollYProgress, [0, 1], [0, -100])
return (
<motion.div style={{ y }}>
スクロールで動く要素
</motion.div>
)
}
パララックス効果もフックを使って簡単に実装できる。ランディングページとかで使うと見栄えが良くなる。
リストアニメーション
import { motion } from "motion/react"
const listVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}
const itemVariants = {
hidden: { opacity: 0, x: -20 },
visible: { opacity: 1, x: 0 }
}
function AnimatedList({ items }) {
return (
<motion.ul
variants={listVariants}
initial="hidden"
animate="visible"
>
{items.map((item, index) => (
<motion.li key={index} variants={itemVariants}>
{item}
</motion.li>
))}
</motion.ul>
)
}
variantsとstaggerChildrenを組み合わせると、リストの各項目が順番にアニメーションする。TODOリストやニュースフィードで使うとQOL上がりますね。
まとめ
Motionは、以下のような場面で特に威力を発揮する:
- Reactアプリケーションに滑らかなアニメーションを追加したい
- パフォーマンスを妥協したくない
- TypeScriptで型安全に開発したい
- ジェスチャーやスクロール連動など、高度なアニメーションを実装したい
正直なところ、CSSアニメーションやWeb Animations APIで頑張ることもできるけど、開発効率を考えるとMotionを使ったほうがコスパ的に良いと思う。MITライセンスで無料で使えるし、公式サイトには100以上のサンプルコードも用意されている。
アニメーションライブラリで迷っているなら、まずはMotionを試してみることをおすすめする。
