bitA Tech Blog
🤹

Anime.js 入門 - その1

に公開

✨ 本記事はbitA Tech Blog Advent Calendar 1日目の記事です。


Web Animations APIの登場により、JavaScriptからDOMを使ったアニメーションの実装や制御はかなり楽に行えるようになりました。しかし、凝ったアニメーションやDOM以外の値をアニメーションさせたい場合、Web Animations APIだけではまだ物足りない部分があります。

この記事では、アニメーション実装の「かゆいところに手が届く」かもしれないライブラリAnime.jsを取り上げて、その基本機能を紹介しています。

JavaScriptベースのアニメーションエンジンAnime.js

Anime.jsは JavaScriptベースのアニメーションエンジンで、DOM 要素だけでなく任意のJavaScriptオブジェクトやSVG属性など、JavaScriptから触れるあらゆる値を補間することができます。

https://animejs.com/

前提条件

  • Anime.js - v4.2.2

インストール方法

Anime.jsは複数のインストール方法が提供されており、NPMからパッケージをインストールしたり、CDNから読み込んで利用することができます。

From NPM

npm install animejs
import { animate } from 'animejs';

記事中では必要に応じて createTimelineutils などを追加でインポートしています。

From a CDN

ES Modules

import { animate } from 'https://esm.sh/animejs';

UMD Global object

<script src="https://cdn.jsdelivr.net/npm/animejs/dist/bundles/anime.umd.min.js"></script>
<script>
  const { animate } = anime;
</script>

UMD 版ではグローバル変数animeが生えるので、そこからanimate 関数などを取り出して使う形になります。

基本的な使い方

基本的なアニメーションは animejs モジュールから animate() 関数を読み込んで利用します。

import { animate } from 'animejs';
const animation = animate(targets, parameters);

animate() のシグネチャは次のとおりです。

  • 第一引数: アニメーションの対象(targets)
  • 第二引数: アニメーションの設定・パラメータ(parameters)

これらの引数にどのような値が指定できるのか、次項から見ていきます。

アニメーションの対象に指定できる値

まず animate() 第一引数で指定できるアニメーション対象(targets)を見ていきます。Anime.jsではCSSセレクタ以外にも、いくつかの指定方法が用意されています。

CSS Selector

document.querySelectorAll() と同じように、CSS セレクタを文字列として指定することができます。複数の要素がマッチする場合も、それらすべてが対象になります。

import { animate, utils } from 'animejs';

animate('.circle:first-child', {
  y: '10px'
});

animate('.circle:nth-child(2)', {
  y: '50px'
});

animate('.circle:nth-child(3)', {
  y: '100px',
  backgroundColor: '#FF4B4B'
});

DOM Elements

document.querySelector()document.querySelectorAll() で取得してきたDOMはそのまま animate() の第一引数に渡せます。

import { animate } from 'animejs';

const $button = document.querySelector('button');
const $squares = $demo.querySelectorAll('.squares');

animate($button, { scale: .75 });
animate($squares, { x: '23rem' });

JavaScript Objects

JavaScript のオブジェクトを直接渡して、そのオブジェクトが持っているプロパティの値をアニメーションさせることができます。

import { animate, utils } from 'animejs';

const vector2D = { x: 0, y: 0 };

animate(vector2D, {
  x: 100,
  y: 150,
});

描画に使う値をオブジェクトで管理しておけば、CanvasやWebGL(Three.js や PixiJS など)のアニメーションにもそのまま活用できます。

Array of targets

animate()では一度に複数の対象を配列で指定することができます。

const vector2D = { x: 0, y: 0 };

// .circleとvector2Dオブジェクトのプロパティxの値を同時にアニメーションさせている
const animation = animate(['.circle', vector2D], {
  x: '100px',
})

複数の対象に対して同じアニメーションを一括で適用できるのがポイントです。

animate() に渡せるプロパティ

animate() の第二引数には、アニメーションさせたいプロパティやアニメーション全体の挙動を調整するためのプロパティを指定します。

CSS Properties

対象の要素のCSSプロパティの値をアニメーションさせます。
値には色や calc() などのCSS関数を含んだ文字列も指定することができます。

import { animate } from 'animejs';

animate('img', {
  left: 'calc(7.75rem * 2)',
  borderRadius: 64,
  'background-color': '#F9F640',
  filter: 'blur(5px)',
});

CSS Transforms

CSSのtransformプロパティに相当するものは、パラメータオブジェクトで個別のプロパティとして指定できます。

translate はショートハンドとして x, y, z プロパティが利用できます。

import { animate } from 'animejs';

animate('img', {
  x: '15rem',
  scale: 1.25,
  rotate: '1turn',
});

animate() 関数は transform にCSSとして渡された文字列を解析しません。transform プロパティに値を一括指定したい場合は、Web Animations API ベースの waapi.animate() を利用します。

import { waapi } from 'animejs';

waapi.animate('img', {
  transform: 'translateX(15rem) scale(1.25) skew(-45deg) rotate(1turn)',
});

CSS Variables

CSS SelectorやDOM Elementsを対象とした場合、CSSのカスタムプロパティも @property で型の定義をせずともアニメーションできます。

animate('img', {
  '--radius': '20px',
  '--x': '16.5rem',
  '--pseudo-el-after-scale': '1.55'
});

カスタムプロパティを利用することで、本来は直接アニメーションできない疑似要素のスタイルなども間接的にアニメーションできるようになります。

HTML属性

HTML属性もアニメーションさせることができます。数値として解釈できる属性は、そのまま補間されます。

import { animate, utils } from 'animejs';

animate('input', {
  value: 1000,
  modifier: utils.round(0),
});

SVG属性

SVGの属性値もアニメーションさせることができます。

import { animate } from 'animejs';

const animation = animate('#nose', {
  rx: '50px',
  ry: '40px',
})

アニメーションできる値

すでに前項のプロパティの説明で掲載したサンプルコードの中でいくつか出てきていましたが、 Anime.jsでは本来プロパティの値と違う型でもアニメーションできる指定方法が存在しています。
ここではそれらの便利な構文を紹介していきます。

animate('.cat', {
  x: '6rem', // 数値
  y: $el => $el.dataset.y, // 関数ベース
  scale: '+=.25', // 相対値
  opacity: {
    from: 0.4, // from / to を持つオブジェクト
  },
});

数値

Number、あるいは単位付きの数値を含んだ文字列を指定できます。

animate(target, {
  width: 100,
  height: '100px',
}); 

単位の自動変換

対象の要素に適用されている値の単位と、 animate() で指定した値の単位が異なる場合でも、Anime.jsは内部で単位を変換してくれます。

import { animate, utils } from 'animejs';

animate('img', {
  width: '25%', // from '48px' to '25%',
  x: '15rem', // from '0px' to '15rem',
  rotate: '.75turn', // from `0deg` to '.75turn',
});

相対値

「今の値から 10px だけ増やしたい」といった相対的な指定は数値の前に接頭辞を追加することで実現できます。

animate('img', { y: '+=90' }); // 今の値からy方向に +90px
animate('img', { y: '-=90' }); // 今の値からy方向に -90px
animate('img', { y: '*=.5' }); // 今のy方向の値に 0.5 掛けた値

Color

色はCSSの <color> 型でよく使われそうなものはだいたいサポートされています。文字列で指定します。

// ref: https://animejs.com/documentation/animation/tween-value-types/color-value

import { animate } from 'animejs';

animate('.hex',  {
  background: '#FF4B4B',
});

animate('.rgb',  {
  background: 'rgb(255, 168, 40)',
});

animate('.hsl',  {
  background: 'hsl(44, 100%, 59%)',
});

animate('.hexa', {
  background: '#FF4B4B33',
});

animate('.rgba', {
  background: 'rgba(255, 168, 40, .2)',
});

animate('.hsla', {
  background: 'hsla(44, 100%, 59%, .2)',
});

関数ベース

関数で値を返すことで、複雑な値の指定を実装することができます。これにより、複数の対象を指定した際に対象ごとに指定する値を変化させる、といったことが簡単に書けます。

animate(targets, {
  x: (target, index, length) => target.dataset.value * (length - index),
});

Tween parameters でアニメーションの挙動を細かく調整する

ここまでで「どのプロパティにどんな値を書けるか」を見てきましたが、Anime.jsでは各プロパティごとに「どう補間するか」を決めるためのパラメータも用意されています。これらはドキュメント上では Tween parameters と呼ばれています。

代表的なものは次のとおりです。

パラメーター名 機能
to ゴールとなる値(デフォルトで指定しているもの)
from 開始値を明示したいときに使う
delay そのプロパティのアニメーション開始を遅らせる
duration そのプロパティのアニメーションにかける時間
ease 補間のカーブ(イージング)
composition 同じプロパティに複数アニメーションが重なったときの合成方法
modifier 補間された値を最終的に適用する前に加工する関数

それぞれのできることを簡単に説明していきます。

to / from - 開始値・終了値を明示する

animate() に直接 { x: 100 } のように書いた場合は「現在値 → 100」に補間されますが、 from を使うと開始値を明示的に指定できます。

import { animate } from 'animejs';

animate('img', {
  x: {
    from: '-50px',
    to: '100px',
  },
  opacity: {
    from: 0.5,
    to: 1,
  },
  scale: {
    from: 2,
    to: 1,
  },
});

こうしておくと、DOM の初期状態に依存せず、アニメーションの開始位置を JavaScript 側でコントロールできます。

delay / duration - プロパティごとの再生時間を変える

Tween parameters として各プロパティに delay や duration を設定することもできます。

animate('img', {
  x: {
    to: '120px',
    delay: 0, // すぐに動き始める
  },
  opacity: {
    from: 0,
    to: 1,
    delay: 300, // 300ms 後にフェードイン開始
    duration: 700, // 700ms かけて不透明に
  },
});

「移動はすぐ始まりつつ、少し遅れてフェードイン」といった、プロパティごとの時間差を細かくつけられます。

ease - プロパティごとにイージングを変える

イージングを設定する ease も Tween parameters としてプロパティ単位で指定できます。

animate('img', {
  x: {
    to: '160px',
    ease: 'out(3)',   // 加速して減速
  },
  rotate: {
    to: '1turn',
    ease: 'linear',   // 一定速度で回転
  },
});

移動はスムーズに、回転は一定速度で…といった表現も簡単です。

composition - 他のアニメーションとの「合成方法」を決める

同じプロパティに対して複数のアニメーションが走っている場合、composition で「既存の値とどう組み合わせるか」を指定できます。

animate('img', {
  x: {
    to: '-60px',
    delay: 500,
    composition: 'replace', // 既存の x のアニメーションをこちらのアニメーションと入れ替える
  },
});

replaceボタンを押すと、xに対して実行されていたアニメーションを中断して別のアニメーションに切り替わっていることがわかります。

modifier - 最終値を加工する

modifier は「補間済みの値 → 実際に適用する値」の間に差し込めるフックです。丸めやクランプ、スナップなどをここで行えます。

import { animate, utils } from 'animejs';

animate('input[type="range"]', {
  value: {
    to: 100,
    modifier: utils.snap(5), // 5刻みでスナップ 5,10,15,20...
  },
});

アニメーションの表現だけでなく「UI として扱いやすい値に変換する」用途でも活躍します。

アニメーションを任意のタイミングで調整する

アニメーションを任意のタイミングで止めたり、再生したり、アニメーション全体の時間をスケーリングしたいという場合は animate() の返り値のインスタンスを利用します。

const animation = animate('img', {
  x: '100px',
});

animation.pause(); // アニメーションを一時停止する

用意されているメソッドは次のとおりです。

メソッド名 効果
play() アニメーションを先頭から前方向に再生する
reverse() アニメーションを後ろ向きに再生する
pause() アニメーションを一時停止する
restart() アニメーションを最初の状態から再生し直す
alternate() 現在の進行方向を反転して再生する
resume() 一時停止中のアニメーションを、停止前の方向で再生する
complete() 強制的に最後のフレームに飛ばす
cancel() 一時停止しエンジンのメインループから外してメモリを解放する
revert() アニメーションをキャンセルし、すべてのアニメーション値を元の状態に戻し、必要に応じてリンクされたCSSインラインスタイルをクリーンアップする
reset() 現在の時刻、進行状況、逆再生、開始時刻、完了時刻のプロパティを一時停止し、デフォルト値にリセットする
seek() アニメーションの現在時刻を更新し、特定の時刻に進める
stretch() アニメーション全体の再生時間を変更する
refresh() 関数ベースで指定された値の再計算を行う

Keyframes で複数ステップのアニメーションを書く

1つのプロパティを「現在 → ゴール」と線形に補間するだけではなく、途中で何度か状態を変えながら動かしたい 場面も多くあります。そのための仕組みが Keyframes です。

Anime.js では大きく 2 パターンの書き方があります。
• プロパティ値に直接配列を渡す Property value keyframes
• アニメーション全体に keyframes オプションを渡す Animation keyframes

Property value keyframes

特定のプロパティだけを「0 → 100 → 200」のように段階的に変えたい場合は、値を配列で渡すのが一番手軽です。

animate('img', {
  x: [0, 100, 200],
  y: [0, 50, 0],    // x と同じタイミングで y も変化
  duration: 2000,
  ease: 'out(3)',
});

もう少し細かく制御したい場合は、各ステップを Tween parameters として書くこともできます。

animate('img', {
  x: [
    {to: 100, duration: 500 },
    {to: 200, duration: 1000, delay: 500},
    {to: 300, duration: 500, delay: 500 }
  ],
  ease: 'out(3)',
});

「最初の300msで素早く動き、その後700msかけてゆっくり止まる」といった、時間配分まで含めた設計が可能になります。

アニメーション全体の再生設定(Playback settings)

ここまでは「どのプロパティをどう補間するか(Tween parameters / Keyframes)」を中心に見てきましたが、Anime.jsには アニメーション全体の再生方法をまとめてコントロールするための設定 も用意されています。ドキュメントではこれらを Playback settings と呼んでいます。
主なものは次のとおりです。

プロパティ名 コントロールできる内容
delay アニメーションの再生開始までの遅延時時間を設定
duration アニメーションの再生時間を設定
loop アニメーションの再生回数
loopDelay アニメーションを繰り返す際の間隔
alternate アニメーションの再生方向を切り替える
reversed アニメーションの再生方向を逆にする
autoplay 自動で再生するかどうか
frameRate アニメーションの更新頻度
playbackRate アニメーション全体に対してのフレームレート
playbackEase アニメーション全体に対してのイージング
persist`(WAAPI のみ) アニメーション終了後の状態を維持するか

Playback settings は animate() の第二引数のオブジェクトのプロパティとして設定します。

一部をここで紹介していきます。

delay / duration — アニメーション全体の時間

delayduration は Tween parameters にも出てきましたが、各プロパティのデフォルトの値としても指定できます。

  • delay: アニメーションの再生が始まるまで待つ時間(ミリ秒)
  • duration: 1ループ分の再生時間(ミリ秒)
import { animate } from 'animejs';

animate('img', {
  x: '100px',
  delay: 500, // 500ms待ってからアニメーション開始
  duration: 3000, // 1秒かけて再生
})

プロパティごとに Tween parametersで delayduration を指定している場合は、Tween parametersの値が優先されます。

loop / loopDelay — 何回 / どのくらい間隔で繰り返すか

アニメーションを繰り返したいときは loop を使います。

animate('.square', {
  x: '175px',
  loop: 3, // 3回繰り返す
});

loop の主な指定方法は次のとおりです。

  • Number: ループ回数(0〜Infinity)
  • Infinity: 無限ループ
  • true: Infinity と同じ(無限ループ)
  • -1: Infinity と同じ

各ループの間に間隔を空けたいときは loopDelay を組み合わせます。

animate('.square', {
  x: '175px',
  loop: true,      // 無限ループ
  loopDelay: 250,  // 各ループ間に 250ms の間隔
});

alternate / reversed — 再生方向を切り替える

アニメーションの再生方向は alternate と reversed で制御できます。

  • alternate
    true で 1ループ目は正方向、2ループ目は逆方向…というように、ループごとに再生方向を反転してくれます。
    loop と組み合わせると「行って帰ってくる」アニメーションを簡単に作れます。
  • reversed
    true でアニメーションを最初から逆方向に再生します。alternate と組み合わせると「逆→正→逆…」のようなパターンになります。
animate('img', {
  x: '170px',
  loop: 3,
  alternate: true,
});

// 最初から逆再生
animate('img', {
  x: '170px',
  reversed: true,
});

autoplay — 自動再生するかどうか

autoplay はアニメーションを作成したタイミングで自動的に再生するかどうかを制御します。

const animation = animate('img', {
  x: '140px',
  autoplay: false, // 生成時には再生しない
});

// 好きなタイミングで再生
button.addEventListener('click', () => {
  animation.play();
});

デフォルトは true なので、ユーザー操作に応じて再生したい UI では autoplay: false にしておくと良いかと思います。

frameRate — アニメーションの更新頻度(FPS)

frameRate は、JS ベースのアニメーション(animate()createTimer() が1秒間に何回更新されるか(FPS)を指定します。

デフォルトは60FPSです。
値を下げるほどフレーム間隔が長くなり、CPU 負荷を抑えられます。高頻度の更新が必要ない場合は frameRate の値を低くします。

import { animate } from 'animejs';

animate('img', {
  x: '180px',
  duration: 2000,
  frameRate: 30, // 30 FPS に制限
});

playbackRate — 再生スピードの倍率

playbackRate はアニメーション全体の再生速度をスケーリングします。

import { animate } from 'animejs';

const animation = animate('.cat', {
  x: '180px',
  duration: 2000,
  autoplay: false,
  playbackRate: 2 // 2倍速で再生
});

const animation = animate('.dog', {
  x: '180px',
  duration: 2000,
  autoplay: false,
  playbackRate: 0.5 // 0.5倍速で再生
});

playbackEase — アニメーション全体に対してのイージング

playbackEase はアニメーション全体に対してのイージングを設定します。

tween property で ease を設定した場合

0 ────────────────────────────────› 1
A ──ease──› B ──ease──› C ──ease──› D

playbackEase を設定した場合

0 ──────────────ease──────────────› 1
A ────────› B ────────› C ────────› D
import { animate } from 'animejs';

animate('.ease', {
  x: {
    to: '300px',
    ease: 'outInBounce',
    duration: 2000,
  },
  rotate: {
    to: '180deg',
    ease: 'outInBounce',
    duration: 2000,
    delay: 2000
  },
});

animate('.playbackEase', {
  x: {
    to: '300px',
    duration: 2000,
  },
  rotate: {
    to: '180deg',
    duration: 2000,
    delay: 2000
  },
  playbackEase: 'outInBounce',
});

コールバックでアニメーションとアプリの状態を同期する

アニメーションのライフサイクルにフックできるコールバック を紹介します。
Anime.js では次のようなタイミングで関数を実行できます。

コールバック名 実行タイミング
onBegin アニメーションの再生が始まったとき
onComplete アニメーションが最後まで再生されたとき
onBeforeUpdate 各フレームの更新直前(内部状態の更新前)
onUpdate 各フレームの更新後
onRender DOM への描画直前
onLoop ループ再生の 1 周が終わったとき
onPause 一時停止されたとき
then() アニメーション完了時に呼ばれる Promise ライクな API

これらはすべて animate() の第二引数オブジェクト内に関数として渡します。

import { animate } from 'animejs';

const animation = animate('img', {
  x: '120px',
  opacity: [0, 1],
  duration: 800,
  loop: 2,
  alternate: true,

  onBegin: (anim) => {
    console.log('start', anim.currentTime);
  },
  onUpdate: (anim) => {
    const progress = Math.round(anim.progress);
    document.querySelector('#progress').textContent = `${progress}%`;
  },
  onComplete: () => {
    console.log('done!');
  },
});

onUpdate などのコールバックにはアニメーションインスタンスが渡されてくるので、currentTime (アニメーションの実行時間) や progress (アニメーションの進捗状況) を使ってプログレスバーやカウンタUIを同期するといったこともできます。

then()Promise と同じように扱えます。複数のアニメーションや非同期処理と組み合わせて「終わったらこの処理をする」といった制御を書きたいときに便利です。

animate('img', { x: '100px', duration: 500 })
  .then(() => animate('.circle', { x: '200px', duration: 500 }))
  .then(() => console.log('all done!'));

おわりに

この記事を執筆するにあたって初めてAnime.jsを触りましたが、とにかくドキュメントの項目一つ一つデモが用意されているためわかりやすく、理解が大変捗りました。
アニメーションの実装に慣れていない人でも触りやすいアニメーションライブラリだと思います。

今回の記事では animate() の機能を中心に解説を行いました。
Anime.jsには、まだまだ紹介できていない機能がたくさんあります。
今後執筆予定の「Anime.js入門 - その2」では時間軸ベースで複数のアニメーションを管理できる Timeline, テキストを1文字づつアニメーションできる TextSplit, 大量の対象に対して「ずらす」アニメーションが作成できる Stagger などを取り上げる予定です。

株式会社ビットエーAdvent Calendar はまだまだ続きます。二日目となる明日の担当は @komeloper で「MariaDBのSpiderストレージエンジンをDockerで試してみた」です!お楽しみに!

bitA Tech Blog
bitA Tech Blog

Discussion