はじめに
CSSのtransitionやanimationだけでは、途中で値を止めて別の値へ滑らかにつなぎ直す、
みたいな「途中経過を自分でコントロールしたい」場面にどうしても手が届きません。
かといって、リッチなReact向けアニメーションライブラリを1つ入れるほどでもない――
そんなときに候補になるのが、フレームワークに依存しない低レベルなアニメーションエンジン
Popmotionです。
Popmotionは数値・色・複雑な文字列(box-shadowやSVGのpath)を、キーフレームや
物理ベースのスプリングでアニメーションさせるための小さなツールキットです。DOMの操作方法は
一切規定しないので、Reactでも素のDOMでもCanvasでも、値の変化さえ受け取れれば
どこにでも組み込めます。
とはいえ、読むより触った方が早いと思います。数値をスプリングで動かす様子と、 慣性で減速していく様子をその場で確認できるサンプルを用意したので、 先に挙動を見たい方はこちらからどうぞ。
Popmotionとは
Popmotionは、Framer Motion(現在はMotion)と同じ作者によって開発された、 JavaScript向けの低レベルアニメーションエンジンです。UIコンポーネントは提供せず、 「値をどう時間変化させるか」だけに責任を絞っているのが特徴です。
主な特徴
- 軽量 - 中核となる
animate関数は約4.5kbで、関数単位でインポートできるため 使わない機能はバンドルに含まれません - 多様な値に対応 - 数値だけでなく、色(
#fff→#000)や"10px 10px 0px rgba(0,0,0,0.2)"のような複合的な文字列もそのままアニメーションできます - 物理ベースのアニメーション -
stiffness(硬さ)・damping(減衰)・mass(質量)を 指定するスプリングアニメーションや、スクロールの慣性のような減速運動(inertia)を 数式レベルで扱えます - TypeScript製で高いテストカバレッジ - 型定義が同梱されており、95%以上の テストカバレッジを謳っています
インストール
npm install popmotion
yarn add popmotion
Popmotionのサンプルを動かす
animate()は、Popmotionの中心となる関数です。fromとtoに値を渡し、
onUpdateコールバックで毎フレームの計算結果を受け取るだけで、キーフレーム・スプリング・
慣性のいずれのアニメーションも同じインターフェースで扱えます。以下は数値をスプリングで
動かす要点部分です。
import { animate } from "popmotion"
animate({
from: 0,
to: 100,
type: "spring",
stiffness: 200,
damping: 10,
onUpdate: (latest) => {
box.style.transform = `translateX(${latest}px)`
},
})
下のサンプルでは、ボタンを押すたびにanimateをtype: "spring"で呼び出し、
四角形を左右に弾ませます。stiffness(硬さ)とdamping(減衰)のスライダーを
動かしてから再度押すと、動きの質感がどう変わるか一目で分かります。stiffnessを上げると
より鋭く跳ね、dampingを下げるとバウンドの回数が増えます。
type: "spring"をtype: "keyframes"に変え、toに[0, 240, 0]のような配列を渡すと、
物理シミュレーションではなく、指定した時間で通過点を辿る従来のキーフレーム
アニメーションに切り替わります。用途に応じて型を選べるのがPopmotionの強みです。
基本的な使い方
Popmotionの最小構成は、animateにfrom・to・onUpdateを渡すだけです。
typeを省略すると、渡されたオプションから自動的にキーフレームかスプリングかが判定されます。
import { animate } from "popmotion"
animate({
from: 0,
to: 100,
duration: 1000,
onUpdate: (latest) => console.log(latest),
onComplete: () => console.log("完了"),
})
数値だけでなく、色やbox-shadowのような複合値もそのままfrom/toに渡せます。
import { animate } from "popmotion"
animate({
from: "#e66465",
to: "#9198e5",
onUpdate: (latest) => {
document.body.style.backgroundColor = latest
},
})
実践的なユースケース
Popmotionは「キーフレームで決まった動きをさせる」「スプリングで自然な動きをさせる」
「慣性でスクロールのような減速を再現する」という3つの異なるアニメーションの型を、
すべて同じanimate系のAPIで扱えます。ここでは、それぞれのパターンを個別のサンプルで確認します。
キーフレームで複数の色を順番に通過させる
toに配列を渡すと、複数の値を順番に通過するキーフレームアニメーションになります。
色の配列を渡せば、グラデーションのように複数色を経由させることも可能です。
進捗バーやタブのハイライト移動のように、決まった区間を決まった時間で動かしたい場面に向いています。
offset: [0, 0.2, 1]のような配列を追加すると、各キーフレームに到達するタイミングを
均等割りではなく好きな比率に変更できます。序盤だけ速く色を変えたい、といった演出に使えます。
inertiaでスクロールのような慣性運動を再現する
inertiaは、初速から徐々に減速していく運動を再現する専用関数です。スマートフォンの
フリックスクロールのように、「離した瞬間の勢い」をそのまま減衰させたいときに使います。
velocityが初速、powerが到達距離の目安、timeConstantが減速の速さを決めます。
velocityを大きくするほど到達距離が伸び、min/maxを指定すると境界でスプリングを使った
バウンド運動に切り替わります。ドラッグ操作の「離した後の余韻」を作りたいときに便利な挙動です。
まとめ
Popmotionは、UIコンポーネントを持たない代わりに「値をどう時間変化させるか」に
特化した低レベルなアニメーションエンジンです。animateひとつでキーフレーム・スプリングを
切り替えられ、inertiaでスクロールのような慣性運動まで再現できます。フレームワークに
依存しないので、Reactに限らずCanvasやSVG操作にも組み込めるのが強みです。
なお、開発の中心は同じ作者によるMotion(旧Framer Motion)に移っており、Popmotion自体の
更新は活発ではありません。React中心のリッチなアニメーションが欲しい場合はMotionも
選択肢に入りますが、フレームワークを問わず値の変化だけを小さく扱いたい場面では、
Popmotionの軽さと低レベルさがそのまま利点になります。まずはanimateひとつから、
手元のプロジェクトに組み込んでみてください。
