アニメーションは画面の遷移やボタンの演出など、あらゆるコンテンツやアプリケーションで利用されています。CSSならanimationtransition、JavaScriptであれば「Anime.js」「jQueryのアニメーション機能」「TweenJS (CreateJS)」といったアニメーションライブラリを利用して実装することが多いと思います。

私はFlashの全盛期にActionScript 3.0で「Tween24」というアニメーションライブラリを自作していたこともあり、アニメーションには人一倍の思い入れがあります。しかし、その仕組みを理解している方は意外にも少ないのではないでしょうか。

ライブラリやフレームワークといったテクノロジーは、仕組みや原理を理解しておくことで備わっている機能をより活用でき、また応用の幅も広がります。今回はアニメーションライブラリの基礎部分の実装を交えながら、アニメーションの仕組みを紹介します。

アニメーションの基本的な仕組みと考え方

アニメーションの仕組みは、時間経過とともに「ある値を0(0%)〜1(100%)の間で連続して変化をさせる」といったものです。具体的には「始点」「時間」「変化量」の3つの概念で考えます。

例えば「画像をX座標100pxから500pxまで2秒で移動させたい」といった場合、「X座標を100(始点)から、2秒かけて(時間)、+400する(変化量)」と考えます。

アニメーションの仕組みのイメージ図

値を変化させるための計算式

アニメーションさせる値は、時間経過に合わせた計算が必要となります。まず「変化にかける時間」と「経過した時間」から「進捗率」を算出します。次に「変化量」に「進捗率」をかけ、「始点」に加えることでアニメーション中の値が計算できます。これがアニメーションさせるための心臓部ともいえる計算式となります。

アニメーションの進捗率 = (経過した時間) / (変化にかける時間)
アニメーション中の値 = (始点) + (変化量) * (アニメーションの進捗率)

あとはこの計算を連続で実行するための機構があれば、アニメーションライブラリの原型が出来上がります。では、JavaScriptを使って具体的な実装方法を紹介していきます。

JavaScriptでの実装方法

今回は、CSSのtransform:translateを使って画像(X座標50px、Y座標50pxに配置されているみたらしくん)を「1秒かけてX座標500px、Y座標150px」まで移動させるアニメーションを作成する想定で紹介します。

まずは、各値の初期設定を行います。

const target = document.getElementById('mitarashi'); // アニメーションの対象
const startX = 50; // X座標の始点
const startY = 50; // Y座標の始点
const endX = 500; // X座標の目標点
const endY = 150; // Y座標の目標点
const diffX = endX - startX; // X座標の変化量
const diffY = endY - startY; // Y座標の変化量
const time = 1000; // 変化にかける時間 (ミリ秒)

次に変化させるための計算式を実装します。この計算は時間経過とともに何度も実行させる必要があるため、今回その機構をrequestAnimationFrame()を使って実現します。

requestAnimationFrame()は引数で渡したコールバック関数を、ブラウザの次の再描画が実行される前に呼び出してくれます。そのため、アニメーションが完了するまでrequestAnimationFrame()を呼び続けることで毎フレーム計算が行われ、結果としてアニメーションとなります。

またコールバック関数には引数として、requestAnimationFrame()最初に発火したタイミングからコールバック関数が呼ばれるまでの経過時間が渡されます。この引数の値をアニメーションの進捗率の算出に利用します。

// アップデートを実行する
requestAnimationFrame(update);

function update(timestanp) {
  // 進捗率を算出
  progress = timestanp / time;

  // 進捗率が100%を超えないよう丸める
  progress = Math.min(progress, 1);

  // 値を算出し反映する
  const resultX = startX + diffX * progress; // X座標
  const resultY = startY + diffY * progress; // Y座標
  target.style.transform = `translate( ${resultX}px, ${resultY}px )`;

  // 進捗が1未満の場合はアップデートを再実行する
  if (progress < 1) {
    requestAnimationFrame(update);
  }
}

ここまで十数行ほどのコードを記載しましたが、たったこれだけのコードでアニメーションさせることができます。試しに実行してみましょう。

これで目的通りのアニメーションができました。しかしライブラリとして使うには、座標や時間を指定するだけで動作するくらい手軽に使いたいものです。次のページでは、より汎用的に使用できるよう手を加えていきます。