Spring AnimationとVanilla JSで始めるFramer Motion
2024 年 11 月 14 日に Framer Motion の作者である、Matt Perry さんが作成した、Motion One と Framer Motion が統合され、Motion という名前になりました。
統合されたことにより、実質 Framer Motion が Vanilla JS や Vue などのフレームワークに依存しないライブラリとして使えるようになりました。
この記事では、Spring Animation と Vanilla JS で Motion を使う方法を紹介します。
Spring Animation の説明を飛ばして、Motion の使い方を知りたい方はMotion を使うからご覧ください。
Framer Motion と Motion One とは?
Spring Animation や Keyframe Animation など、CSS では表現しにくかったアニメーションを実装することができるライブラリです。
Framer Motion は、React を前提としたライブラリでしたが、Motion One は Vanilla JS や Vue などのフレームワークに依存しないライブラリとして作成されました。
これらが統一され、Motion One
はMotion
に、Framer Motion
はMotion for React
になりました。
Spring Animation について
iPhone の Dynamic Island などの弾むようなアニメーションがSpring Animation
です。
Spring Animation
の説明だけで 1 つの記事が書けるほどの内容なので、かなり割愛して説明します。
CSS Animation との違い
Spring Animation
は、現実世界のバネのような流動的で自然な物理法則に基づいたアニメーションを表現することができるアニメーションです。
CSS Animation
では、キーフレームとアニメーションの時間を用いた、ベジェ曲線のようなアニメーションしか表現できませんでした。
Spring Animation
では、stiffness(バネの強さ)
とdamping(バネの減衰)
、mass(バネの質量)
という 3 つのパラメータを用いて、オブジェクト的にアニメーションを表現することができます。
<motion.div
transition={{
type: "spring", // アニメーションのタイプを指定
stiffness: 100, // バネの強さ
damping: 10, // バネの減衰
mass: 1, // バネの質量
}}
/>
Apple が考えた Spring Animation
上記で説明したように、Spring Animation
は、stiffness(バネの強さ)
とdamping(バネの減衰)
、mass(バネの質量)
という 3 つのパラメータを用いて、表現することができます。
ですが、これらは直感的ではないため、Appleはbounce(弾む力)
とduration(アニメーションの時間)
という 2 つのパラメータを用いて、スプリングアニメーションを表現する方法を考えました。
<motion.div
transition={{
type: "spring", // アニメーションのタイプを指定
bounce: 0.15, // 弾む力
duration: 0.75, // アニメーションの時間
}}
/>
3つのパラメーターと2つのパラメーターは、それぞれ数式で変換することができます。
詳しくは以下の動画をご覧ください。
Spring Animation はなにが良いのか?
Spring Animation
は、バネのように弾むだけが良いポイントではありません。
以下の 3 つのポイントが良いと言われています。
- アニメーションの終了が非常に自然である
- アニメーションを不自然な動きをすることなく、中断することができ、次にアニメーションにスムーズに移行することができる。
- 現実世界の物理法則に基づいたアニメーションを表現することができる
bounce にはどの値を設定すればいいのか?
まず、bounce
は 0 から 1 の値を設定することができます。
基本的には、0 を設定しましょう。Spring Animation
は上記で述べたように、弾むのが利点ではないです。
すこし弾むようにしたい場合は、最大でも 0.15 を設定しましょう。
(個人の感覚です)
これ以上書くと記事の本質からずれてしまうため、機会があれば別の記事で詳しく書きたいと思っています。
Motion を使う
インストール
npm install motion
なんと、motion/mini
は脅威の 2.5kb です。GSAP 同等品よりも 90%小さいそうです。
Tiny
Mini animate is 90% smaller than its GSAP equivalent, scroll 75% smaller.
基本的な使い方
animate
を import し、そこに対象の要素とアニメーションを指定します。
import { animate } from "motion";
const target = document.querySelector(".target");
// 360度回転する
animate(target, { rotate: 360 });
target には、セレクターを指定することもできます。
animate(".target", { rotate: 360 });
Spring Animation の実装
animate
の第三引数にtransition
を指定することで、Spring Animation を実装することができます。
import { animate } from "motion";
const target = document.querySelector(".target");
// 1秒かけて360度回転する
animate(target, { rotate: 360 }, { type: "spring", bounce: 0, duration: 1 });
Spring なし | Spring あり | 同時実行 |
---|---|---|
Spring
を指定したほうが、より自然なアニメーションになります。
特にアニメーションの終了部分が自然に見えます。
Spring
を指定しない挙動は、不自然で、少し物理的な挙動とは違うものになります。
Spring
を少し強くしてみましょう。
animate(target, { rotate: 360 }, { type: "spring", bounce: 0.15, duration: 1 });
Spring なし | Spring 強め | 同時実行 |
---|---|---|
若干強くなったので、終わり部分がバネのように弾むようになりました。
FadeIn アニメーション
画面に表示された時に、フェードインするアニメーションを実装します。
inView
を使うことで、画面に表示された時にアニメーションを実行することができます。
import { inView } from "motion";
inView(
".fadein",
(info) => {
animate(
info.target,
{ opacity: 1, y: [32, 0] },
{ duration: 0.75, type: "spring", bounce: 0 }
);
},
{ margin: "0px 0px -120px 0px" }
);
上記のように、第1引数にターゲットとなる要素を指定し、第2引数に画面内に入った時に実行する関数を指定します。
関数の中では、animate
を使って、アニメーションを指定します。
y
には、[32, 0]
のように、動きをつけるとより自然なアニメーションになります。
弱めのblur
をつけるのも良いかもしれないです。
animate(
info.target,
{ opacity: 1, y: [32, 0], filter: ["blur(4px)", "blur(0px)"] },
{ duration: 0.75, type: "spring", bounce: 0 }
);
inView
の詳細は以下のドキュメントをご覧ください。
スクロールアニメーション
指定した要素がスクロールされた時に、アニメーションを実行することができます。
import { scroll } from "motion";
scroll((progress) => console.log(progress));
progress
は、0 から 1 のスクロール量が返されます。
1 の場合は、スクロールが完了したことを表します。
transform
を使う
スクロール量に応じて、アニメーションを実行する場合は、transform
を組み合わせて実装します。
transform
は、Motion
からexport
されている、Utils
です。
この関数は、入力値を別の出力に変換してくれます。便利です。
import { transform } from "motion";
const transformNumber = transform(
[0, 1], // Input
[0, 100] // Output
);
transformNumber(0.5); // 50
上記の場合は、0 から 1 の値を、0 から 100 の数値に変換してくれます。
例えば、0.5 の場合は、50 になります。
上記の関数を使って、スクロール量に応じて、アニメーションを実行します。
試しに以下の3種類のアニメーションを実装します。
1. 色が変わりながら、回転するアニメーション
import { transform } from "motion";
scroll((progress, _) => {
// 0から1のスクロール量を、0度から360度の回転量に変換
const rotate = transform([0, 1], ["0deg", "360deg"]);
// 0から1のスクロール量を、色の変化に変換
const color = transform(
[0, 0.3, 0.6, 0.9, 1],
["#cc0000", "#66cc00", "#00cccc", "#6600cc", "#cc00cc"]
);
animate(
box,
{
background: color(progress),
rotate: rotate(progress),
},
{ type: "spring", bounce: 0.1, duration: 1 }
);
});
入力と出力の配列の長さは、同じである必要があります。
2. 進捗度を表現するアニメーション
import { transform } from "motion";
scroll((progress, _) => {
animate(progressCircle, {
pathLength: progress,
});
});
SVG の Circle を使って、進捗度を表現するアニメーションです。
Path の長さを変更することで、進捗度を表現します。
3. 縦スクロール量を横スクロールに変換するアニメーション
import { transform } from "motion";
scroll((progress, _) => {
const translateX = transform([0, 1], ["0%", "-80%"]);
animate(target, { x: translateX(progress) });
});
とても簡単ですね。
このように、スクロール量に応じて、アニメーションを実行することができます。
まとめ
新しく統合されたMotion
とSpring Animation
を紹介しました。
紹介した以外にもたくさんのUtils
やアニメーションに適した関数があります。
Spring Animation
を使うことによって、より自然なアニメーションを実装することができます。
ぜひ、Motion
を使って、より自然なアニメーションを実装してみてください。
それでは良いMotion
ライフを!
参考
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion
css animationと比較するよりも javascript側にある ウェブアニメーションAPIと比較したほうが良いのではないでしょうか……?(特に Promise周りがどうなっているか気になる