1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Motionを触ってみる

Last updated at Posted at 2024-12-02

viviONグループでは、DLsiteやcomipoなど、二次元コンテンツを世の中に届けるためのサービスを運営しています。
ともに働く仲間を募集していますので、興味のある方はこちらまで。

:thinking: Motionとは

MotionはFramer MotionとMotion Oneが統合されて生まれたアニメーションライブラリです。ユーザーの操作や状況に併せたアニメーションを簡単に実装できるのが特徴です。

元々Framer(ノーコードのWebサイト作成ツール)から提供されていたアニメーションライブラリでしたが、作者のMatt Perry氏がFramerから独立したタイミングで、ライブラリも独立しました。

Framer MotionはReact向けのアニメーションライブラリでしたが、MotionではVanilla JS向けのAPIが提供されています。
※Motion Oneの機能がMotionとして提供されている形ですが、Framer MotionはMotion Oneを単純にラップしているだけでもないので、同等の機能を提供するわけではありません。

本記事では、Vanilla JS向けのAPI(Motion Oneの機能)を使ったアニメーションの実装方法を紹介します。

:wrench: 環境構築

環境構築はViteのテンプレートを使って行います。以下のコマンドを実行して、プロジェクトを作成します。

npm create vite@latest my-motion -- --template vanilla
cd my-motion
npm install motion

CDNを使って読み込むこともできます。
今回はViteを使っていますが、LPなど簡易的なWebサイトの制作ではCDN呼び出しを使っても良さそうです。

<script src="https://cdn.jsdelivr.net/npm/motion@11.11.17/dist/motion.js"></script>
<script>
  const { animate, scroll } = Motion
</script>

jsファイル上でESモジュールを直接読み込むこともできます。

import { animate, scroll } from "https://cdn.jsdelivr.net/npm/motion@11.11.17/+esm";

Motionはとても軽量で、すべての機能を使ってもgzip圧縮されたサイズは約17kbです。GSAP(約23kb)と比較しても軽量ですし、tree-shakingも効くので、よりサイズは小さくなります。
機能が制限される代わりにバンドルサイズを落としたmotion/mini(約2.5kb!)も提供されており、合わせて利用することもできます。

:robot: バージョン確認

"motion": "^11.11.17"

:school: motionの基本的な使い方

もっとも簡単にMotionを使う方法は、animate関数を使ってアニメーションを定義することです。

import { animate } from "motion";

// Element(s)
const box = document.getElementById("box");

animate(box, { opacity: 0 }, { duration: 0.5 }); // 0.5秒でopacityを0にする

// Selectors
animate("div", { x: [0, 100] }, { ease: "easeIn" }); // 100px右に移動

第一引数にアニメーションを適用する要素、第二引数にアニメーションのプロパティ、第三引数にオプションを定義します。

アニメーションのプロパティには数値を配列で渡すことで、アニメーションの開始と終了の値を指定できます。

オプションからは、アニメーションの挙動を調整できます。

1.gif

:compression: 便利な関数

ユーザーインタラクションに応じたアニメーションを実装するために、Motionには便利な関数が用意されているので幾つか紹介します。

scroll

animate関数から返されるAnimationPlaybackControlsを渡すことで、スクロール時のアニメーションを実装できます。

import { animate, scroll } from "motion";

const box = document.getElementById("box");
const animation = animate(box, { opacity: 0 });

scroll(animation);

アニメーションの進行度は、スクロールの進行度と連動します。

第二引数でオプションを渡すことで、連動する対象の要素やスクロールの軸、進行度の範囲を指定できます。

2.gif

inView

要素がビューポートに入った際、callback関数を実行します。
実装的にはIntersection Observer APIのラッパーっぽいです。

import { inView } from "motion";

const box = document.getElementById("box");

inView(box, () => {
  console.log("in view");
});
3.gif

inViewに渡す関数は、通常要素がビューポートに入った際、一度だけ実行されます。
このcallback関数内で関数を返すことで、要素がビューポートから外れた際に実行される関数を定義できます。

inView(box, () => {
  console.log("in view");

  return () => {
    console.log("out of view");
  };
});

この場合、ビューポートを出入りするたびにcallbackが実行される様になります。

stagger

複数要素をズラしてアニメーションしたい場合、stagger関数を利用することで簡単に実現できます。

import { animate, stagger } from "motion";

animate(
  ".item",
  {
    opacity: [0, 1],
    translateY: [100, 0],
  },
  {
    duration: 0.5,
    type: "spring",
    delay: stagger(0.1),
  }
);
3.gif

spring

spring関数を使うことで、スプリングアニメーションを実装できます(ばねのような挙動)

import { animate, spring } from "motion";

animate(div, { x: 100 }, { type: spring });
4.gif

※motionではデフォルトで使えるアニメーションです(type: "spring"としても同じ)

:space_invader: Motionを使ったUIアニメーション

Motionの基本的な使い方を紹介しましたが、これだけでもアニメーション実装で十分な機能が揃っていることがわかりました。
ここからは、幾つかUIアニメーションの実装例を紹介します。

通知アニメーション

簡単な通知表示にアニメーションを適用してみました。

See the Pen easy notify animation by tenori (@tenorichan) on CodePen.

animate関数はPromiseを返すので、ユーザーのアクションに応じたアニメーションを簡単に実装できます。

async delete(id) {
  const targetEl = document.querySelector(`.notify[data-id="${id}"]`);
  await animate(targetEl, { opacity: [1, 0], x: [0, 100] });
  this.notifyArray = this.notifyArray.filter((item) => item.id !== id);
  this.render();
}

ハンバーガーメニュー

ハンバーガーメニューのアニメーションを実装してみました。

See the Pen global menu open/close animation by tenori (@tenorichan) on CodePen.

ここでもPromiseが活用できます。
stagger関数を用いることで細かい計算を放棄できる点も嬉しい。

await animate(
  ".bg div",
  { width: "100%" },
  { duration: 0.5, type: "spring", delay: stagger(0.1) }
);
await animate(
  ".global .menu li",
  {
    opacity: [0, 1],
    y: [-100, 0],
  },
  { delay: stagger(0.1) }
);

ここではopacityの指定などを配列で行っています。
from~toの関係で記述してeaseの設定値に従い変化しますが、offsetを用いる事でタイミングを自分で設定することもできます。

animate(
  particle,
  { opacity: [0, 1, 0] },
  { offset: [0, 0.9, 1], duration: 0.7 }
};

SVGアニメーション

SVGをアニメーションさせることもできます。
こちらの実装例はドキュメントに記載された実装例とほぼ同じです。

See the Pen favorite button by tenori (@tenorichan) on CodePen.

単純な移動からfillの変更、flubberのような(2点間のパスを補完してくれる)ライブラリを用いることで、モーフィングアニメーションも実現できます。

:mortar_board: 終わり

元々Framer MotionはReactで宣言的に書けるのが強みのライブラリでしたが、MotionのVanilla JS向けAPI(Motion One)はかなり命令的な書き方になっています。VueやSvelteで利用する場合は、宣言的に書けるようなラッパーがないと辛くなりそうです。

一方でペライチLPなどをよりリッチにしたい場合には必要十分で、ユーザーの操作や状況に応じたアニメーションを簡単に実装できるのは魅力的です。

Framer Motionの記事はたくさんあるので、React向けAPIであるmotion/reactについては触れませんでした。
ですがReactで利用する場合にmotion/reactは宣言的にシンプルな記述で自由度の高いアニメーションを実装できるので、オススメのライブラリです。

ドキュメントも豊富で、カスタマイズ要素も多くあるので色々な表現に利用できます。よりリッチなアニメーションを実装したい場合には、是非利用してみてください。

:books: 参考

:rice_ball: 一緒に二次元業界を盛り上げていきませんか?

株式会社viviONでは、フロントエンドエンジニアを募集しています。

また、フロントエンドエンジニアに限らず、バックエンド・SRE・スマホアプリなど様々なエンジニア職を募集していますので、ぜひ採用情報をご覧ください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?