汎用的に利用するための関数化

汎用的に使えるようにするため、「アニメーションの対象」「目標となる座標」「アニメーションの時間」を引数として渡すだけで動作する関数にまとめていきます。イメージとしては、以下のコードで実行できる形を目指します。

// 例:みたらしくんを1秒後に1秒かけて座標(500, 150)へアニメーションさせる
animateTranslate('mitarashi', 1000, 500, 150);

まずは前ページで作成したコードを関数で包みます。合わせて、引数で渡される変数の宣言は不要になるため削除します。

function animateTranslate(targetID, time, endX, endY) {
  const target = document.getElementById(targetID); // アニメーションの対象
  const startX = 50; // X座標の始点
  const startY = 50; // Y座標の始点
  const diffX = endX - startX; // X座標の変化量
  const diffY = endY - startY; // Y座標の変化量
  let progress = 0;

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

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

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

    // 値を算出し反映する
    if (progress >= 0) {
      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);
    }
  }
}

次に、直接指定していた「座標の始点」を動的に取得するよう改修します。ここでアニメーション対象のtransform:translateを参照する必要があるのですが、transformをJavaScriptで取得するとMatrixの文字列として返ってきてしまいます

const style = getComputedStyle(target); // 対象のスタイルを取得
const transform = style.transform; // transform を取得

// transform を出力すると以下の文字列となっている
// matrix(1, 0, 0, 1, 50, 50)

Matrixの詳しい説明はここでは割愛しますが、5・6番目の値が座標を示しています。これを扱いやすい形にするために、正規表現を使って不要な文字列を削除し配列に変換します。配列に変換しても中身は文字列のままとなっているため、変数に代入する際にparseFloat()を使って数値に変換しておきます。

const style = getComputedStyle(target); // 対象のスタイルを取得
const reg = /matrix\((.*)\)/; // 不要な文字列を取り除くための正規表現
const transform = style.transform.match(reg)[1].split(','); // transform を配列として取得
const startX = parseFloat(transform[4]); // X座標の始点
const startY = parseFloat(transform[5]); // Y座標の始点

これで汎用的に使う準備は整いました。しかし機能的に少しさみしいので、再生タイミングを遅らせる遅延機能を追加してみましょう。

再生を遅延させる機能の実装

実行する際の引数を1つ追加し、delayという値で受け取ります。これを進捗率の計算の際に経過時間から減算します。こうすると遅延中は進捗率がマイナス値になるため、進捗率がプラスの場合のみ値が更新されるよう分岐させます。

// 遅延時間を含めた進捗率を算出
progress = (timestanp - delay) / time;

// 進捗率がプラスの場合のみ値を算出し反映する
if (progress > 0) {
  const resultX = startX + diffX * progress; // X座標
  const resultY = startY + diffY * progress; // Y座標
  target.style.transform = `translate( ${resultX}px, ${resultY}px )`;
}

これで遅延再生の機能も実装され、アニメーションライブラリの原型となるものが作成できました。試しに、1秒遅延されるよう指定し実行してみるとこのような動作になります。

// みたらしくんを1秒後に1秒かけて座標(500, 150)へアニメーションさせる
animateTranslate('mitarashi', 1000, 500, 150, 1000);

まとめ

アニメーションライブラリと聞くと複雑な実装を行っている印象を受けるかもしれませんが、蓋を開けてしまえば仕組みはとてもシンプルなものです。

アニメーションに限らずライブラリやフレームワークといったテクノロジーはとても便利なものですが、無闇に使うだけでは機能の持ち腐れとなってしまうこともあります。仕組みや原理、また作者の意図や方針を理解することでテクノロジーとよりよい関係が築け、あなたのプログラミングを一層サポートしてくれるものになるでしょう。

ICS MEDIAではトゥイーンライブラリについて、記事「ウェブのアニメーション制作に役立つ! 流行りのJSライブラリのまとめ」や「最速のアニメーションライブラリはこれだ! JSライブラリの性能をDOMとWebGLで比較検証」にまとめています。今回の記事は原理の紹介でしたが、実際にウェブサイトの制作で利用するのはJSライブラリが多いでしょう。参考してみてください。

次回の記事ではイージングなど、より実用的にアニメーションを作成できる機能の実装方法を紹介したいと思います。