これでできる! クロスブラウザJavaScript入門

第17回 アニメーションの基礎

この記事を読むのに必要な時間:およそ 4 分

アニメーションのライブラリ化

さて,実際にアニメーションさせたいときはjQueryやJSTweenerなどを使えばよいのですが,折角なのでシンプルなライブラリにしてみましょう。ライブラリといっても,汎用的なアニメーション関数程度のものです。

ライブラリ化にあたって,1つのタイマー(setInterval)だけで動くように注意します。これは,ブラウザによって(主にIEにおいて)はパフォーマンスに大きな差が出るためです。

アニメーション関数

/**
 *  アニメーション関数
 *  @param target 対象オブジェクト(nodeのstyleなど)
 *  @param properties プロパティの定義
 *  @param options アニメーションのオプション(省略可)
 */
function MiniTweener(target, properties, options){
  // 入れ物となるオブジェクトを用意
  var item = {};
  item.target = target;
  item.properties = properties;
  item.options = options || {};
  // プロパティの入れ物も用意
  item.props = [];
  for (var name in properties){
    // 各プロパティをオブジェクトにまとめ直して配列に挿入
    // プロパティごとに初期値と最終値,接頭辞,接尾辞を持つ
    var prop = properties[name];
    item.props.push({
      name    :name,
      from    :prop.from,
      to      :prop.to,
      distance:prop.to - prop.from,
      prefix  :prop.prefix || '',
      suffix  :prop.suffix || 'px'
    });
  }
  // アニメーションのオプション定義
  item.duration = item.options.duration || 1000;
  item.easing = item.options.easing || MiniTweener.easing;
  item.onComplete = item.options.onComplete;
  // 開始時刻
  item.begin = new Date() - 0;
  // アニメーションの定義セットを配列に入れる
  MiniTweener.Items.push(item);
  if (!MiniTweener.timerId){
    // アニメーションが開始していなければ開始
    MiniTweener.start();
  }
  return item;
}
MiniTweener.easing = function(time, from, dist, duration){
  return dist * time / duration + from;
};
MiniTweener.FPS = 60;
MiniTweener.Items = [];
MiniTweener.loop = function(){
  var items = MiniTweener.Items;
  var now  = new Date() - 0; // 現在時刻
  var time;
  var n = items.length;
  // アニメーションが終了した時に定義を配列から削除するので,
  // 削除しても添え字に影響が出ないように配列を後ろから走査する
  while (n--){
    var item = items[n];
    // 経過時刻
    time = now - item.begin;
    if (time < item.duration){
      // 各プロパティの途中経過を求め,反映
      for(var i = 0; i < item.props.length;i++){
        var prop = item.props[i];
        var current = item.easing(time, prop.from,
                      prop.distance, item.duration);
        item.target[prop.name] = prop.prefix +
                                 current + prop.suffix;
      }
    } else {
      // 最終的な値を設定
      for(var i = 0; i < item.props.length;i++){
        var prop = item.props[i];
        item.target[prop.name] = prop.prefix +
                                 prop.to + prop.suffix;
      }
      // 終了後のコールバック
      if (item.onComplete){
        item.onComplete();
      }
      // 配列から削除
      items.splice(n, 1);
    }
  }
  if (!items.length){
    // アニメーション定義が空になっていたら停止
    MiniTweener.end();
  }
};
MiniTweener.start = function(){
  // TimerのIDを格納しておく
  MiniTweener.timerId = setInterval(MiniTweener.loop,
                        1000 / MiniTweener.FPS);
};
MiniTweener.end = function(){
  MiniTweener.Items = [];
  // タイマーを停止
  clearInterval(MiniTweener.timerId);
  delete MiniTweener.timerId;
};

少々長いですが,コメントなどを除けば70行程度しかありません。また,ブラウザごとの分岐処理などもありません(実際にCSSを操作する上ではIE用の対応などが必要になることは多いですが⁠⁠。

なお,記述がシンプルになるのでアニメーション関数のプロパティとしてメソッドや配列などを定義していますが,もちろん通常のオブジェクトに各メソッドを定義する実装でも構いません。

この関数を使うときは次のように呼び出します。

アニメーション関数の使い方

MiniTweener(element.style, {
  top  :{from:100, to:200},
  left :{from:50 , to:100, suffix:'%'},
},{
  duration: 500,
});

第一引数に操作対象の要素のstyleオブジェクトを渡し,第2引数でそのプロパティと値を定義して,第3引数でアニメーションの定義を決定しています。

最後に簡単なアニメーション・デモを紹介します。

アニメーションのサンプル

var node = document.getElementById('cbjs-17-5');
var timerId = setInterval(function(){
  var i = node.cloneNode(true);
  // ランダムな文字を生成
  var s = Math.floor(Math.random()*36).toString(36);
  i.appendChild(document.createTextNode(s));
  node.parentNode.appendChild(i);
  // アニメーション適用
  MiniTweener(i.style, {
    top     :{from:100,to:200*Math.random()},
    left    :{from:50 ,to:100*Math.random(), suffix:'%'},
    fontSize:{from:50 ,to:300*Math.random(), suffix:'%'}
  }, {
    duration: Math.random()*1000,
    onComplete:function(){
      // アニメーションが終わった要素を削除
      node.parentNode.removeChild(i);
    }
  });
}, 10);

まとめ

今回はアニメーションの基礎とそれをベースにライブラリ化を行ってみました。次回はアニメーションの実用的なケースとCSS3に関係した部分について解説します。

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/