見た目の仕上げの話をします。ただ、この回で扱いたいのはReactだから必要になる部分だけです。
まずCSSで足りる
押したときの色の変化、出てくるときのふわっと——このあたりはCSSのままで動きます。Reactにしたからといって書き方は変わりません。
<div className="transition hover:scale-105 hover:shadow-md">Tailwindならtransitionとanimate-*がそのまま使えます。まずここで足りないかを考えるのが順番です。
Reactだと書けないこと——消えるとき
困るのは消えるときです。
{isOpen && <div className="transition-opacity">メッセージ</div>}isOpenがfalseになった瞬間、Reactは要素をDOMから消します。消えたものにアニメーションはかけられません。「ふわっと出るのに、消えるときはパッと消える」——これがReactでよくある片手落ちです。
自分で書くなら「消すのを300ミリ秒待ってから消す」という管理が要ります。これを引き受けてくれるのがアニメーションライブラリです。
npm install --save-exact motionMotion(旧Framer Motion)を使います。
import { AnimatePresence, motion } from 'motion/react'
<AnimatePresence>
{items.map((item) => (
<motion.li
key={item.id}
initial={{ opacity: 0, x: -8 }} // 出る前
animate={{ opacity: 1, x: 0 }} // ふだん
exit={{ opacity: 0, x: 8 }} // 消えるとき
>
{item.name}
</motion.li>
))}
</AnimatePresence><motion.li>は「アニメーションできる<li>」です。divでもbuttonでも同じように書けます。
並び替えの動き
layoutを1つ足すと、位置が変わったときに勝手に移動アニメーションが付きます。
<motion.li layout key={item.id}>検索でカードが絞りこまれる、お気に入りが上に来る——残ったカードがスッと動くのはこれです。自分でCSSで書こうとすると、移動前後の座標を測る処理が要ります。
ページの切りかえ
react-routerを使っているなら、View Transitionsという標準の仕組みに乗るのが手軽です。
<Link to={`/birds/${bird.id}`} viewTransition>viewTransitionを付けるだけで、ページの切りかえがふわっと繋がります(対応していないブラウザでは何も起きません=そのまま切りかわるだけ)。
細かく制御したいときは、CSSで名前を付けます。
::view-transition-old(root) { animation-duration: 200ms; }動きを減らす設定に従う
OSには「視差効果を減らす」(Reduce Motion)という設定があります。動きで具合が悪くなる人のための設定なので、無視してはいけません。
import { useReducedMotion } from 'motion/react'
function BirdCard() {
const shouldReduceMotion = useReducedMotion()
return (
<motion.div
animate={{ opacity: 1 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
>useReducedMotion()は、その設定が有効ならtrueを返します。動きを消すか、うんと短くするのが対応です。
CSSだけで書いている部分は、メディアクエリで同じことをします。
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}入れる前に考えること
| やりたいこと | 手段 |
|---|---|
| ホバー・押したときの変化 | CSS(Tailwindのtransition) |
| 出てくるときのふわっと | CSSでも書ける |
| 消えるときの動き | AnimatePresence |
| 並び替え・移動 | layout |
| ページの切りかえ | View Transitions(viewTransition) |
| ドラッグ・複雑な連鎖 | Motionなどのライブラリ |
ライブラリを入れればその分ファイルは大きくなります。「消えるときの動き」が要らないなら、入れないほうが速い——という判断もふつうにあります。読みこむ量が気になるなら、motion/reactから使うものだけを取り込む形にして、公開の回で見たビルド後のサイズを実際に見比べてください。
公式ドキュメントも見てみる
Motion外部サイトAnimatePresence | React exit animations | Motion for ReactMotion公式のAnimatePresence。「消えるときのアニメーション」を扱うページで、keyが必要な理由もここに書かれています(英語)このレッスンのまとめ
- ホバーや出現はCSSのままで足りる。まずそこを確かめる
- Reactで書けないのは消えるとき——Reactが先に要素を消してしまうから
AnimatePresenceが要素を預かり、動きが終わってから消す。keyが必須layoutを1つ足すと並び替えの移動が付く- ページの切りかえは
viewTransition(対応していないブラウザでは何も起きない) - 動かすのは
transformとopacityだけ。widthやtopはカクつく useReducedMotion()で「動きを減らす」設定に従う。CSS側はprefers-reduced-motion- 目安は200〜300ミリ秒。全部を動かさない
次は、コードの置き場所と分け方です。