スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

独自の tweener を作成してアニメーションをカスタマイズ

tweener とは jQuery.Animation()の中で tween オブジェクトを 作成する関数で、jQuery 1.8 から導入されました。 tweener を独自に作成して jQuery に登録することで、 デフォルトの tweener の代わりにその登録した tweener を jQuery.Animation()に使用させることができます。 つまり tweener を定義・登録することで tween オブジェクトを自由に定義することができ、 結果アニメーションをカスタマイズすることができます。

主に jQuery 2.0.3 を使って調べたので、他のバージョンでは 当てはまらないことがあるかもしれませんのでご注意下さい。 この機能は現時点で公式にはドキュメント化されていませんので、 将来告知無しで変更される恐れがあります。

tweener について

tween オブジェクトを作成(して animation オブジェクトに登録) する関数です。 コンテキスト(this)は animation オブジェクトで、 引数としてアニメーションさせるプロパティの名前と その目的値(単位指定した場合は単位付き)が渡されます。 (animation オブジェクトはアニメーションに関する情報を 格納したオブジェクトです。 .animate() のページに概要が書いてあります)

// イメージとして下のような呼ばれ方をする
tweener.call( animation, prop, value );

tweener はプロパティごとに登録され実行されます。 デフォルトではデフォルト tweener が全てのプロパティに対して 実行されます。 あるプロパティに対して複数の tweener が登録されている場合は 最後に登録した tweener から順に実行しようとします。 そのため、独自に登録した tweener → デフォルトの tweener の順に実行しようとします。 しかし、ある tweener が「trueになるような値」 (作成した tween オブジェクトなど)を返すと後続の tweener は 実行されなくなります。 そのため、独自の tweener が tween オブジェクトを返すように すればデフォルトの tweener を実行させないようにできます。

リストに戻る

tweener の追加方法

tweener を格納しているオブジェクトには直接アクセスできないので 登録用の関数jQuery.Animation.tweener()を 使って登録します。

// tweener を登録する関数
jQuery.Animation.tweener( props, callback )
//  props: 文字列。この tweener を実行するプロパティ
//         複数指定する場合は 半角空白一つで区切る
//         未指定の場合は 全プロパティで実行される
//  callback: tweener 本体(関数オブジェクト)

引数には、登録したいプロパティ名と tweener 本体である 関数オブジェクトを渡します。複数のプロパティに対して 登録したい場合は空白で区切って指定します。 プロパティ名を渡さず tweener 本体だけを渡した場合は 全てのプロパティに対して登録します。

リストに戻る

tweener 作成のポイント

作成した tween オブジェクトに run メソッドを定義する

jQuery のアニメーションは tween オブジェクトの run メソッドを 一定間隔で実行してプロパティの値を変更することで実現しています。 そのため run メソッドの挙動を変更することでアニメーションを カスタマイズできます。

run メソッドのコンテキスト(this)は tween オブジェクトで、引数としてアニメーションの進捗状況が 渡されます。進捗状況は0から1の 数値で、0が開始で1が完了を表します。 アニメーションがつつがなく完了した場合や、 引数 gotoEnd をtrueにして.stop()が 実行された場合、 .finish()が実行された場合は 1が渡されて実行されます。

tween.run = function( percent ){
  // percent: 数値。アニメーションの進捗状況を表す。0 から 1。
  // ここでプロパティの現在値を算出して要素などのオブジェクトに
  // プロパティの値を設定している
}
リストに戻る

animation オブジェクトの tweens プロパティに 作成した tween オブジェクトを格納する

tween オブジェクトの run メソッドは jQuery.Animation()が定義する関数 tick が 実行するのですが、その関数 tick は animation オブジェクトの tweens プロパティ(配列)から tween オブジェクトにアクセスします。 tweener が作成した tween オブジェクトは今のところ jQuery 側で tweens プロパティに格納してくれないので、 jQuery.Tweenオブジェクトを流用しない限りは tweener 側で格納する必要があります。

デフォルトの tweener では animation オブジェクトの createTween メソッドを呼び出して tween オブジェクトを 作成しています。この createTween メソッドは jQuery.Tweenオブジェクトを作成し、 そのオブジェクトを tweens プロパティに格納してくれます。 jQuery.Tweenオブジェクトを流用する場合は デフォルト tweener に倣って animation オブジェクトの createTween メソッドを呼び出して tween オブジェクトを 作成するといいと思います。

// tweener 内で tween オブジェクトを animation オブジェクトの
// tweens に格納する
var tween = createMyTween();
this.tweens.push( tween );
// ↑ tweener の this は animation オブジェクト

// jQuery.Tween オブジェクトを流用する場合は
// 以下でOK
var tween = this.createTween( prop, value );
// tween への格納は createTween が行ってくれる
リストに戻る

注意点

この機能はドキュメント化されていない

冒頭で書いたようにドキュメント化されてません。 jQuery のバージョンが進めばこの機能が突然無くなることも 充分ありえますので、使用は自己責任でお願いします。

リストに戻る

一度 tweener を登録したら戻せない

登録用のメソッドについては jQuery.Animation.tweener()が あるのですが削除用のメソッドが見当たりません。 登録先の tweeners オブジェクトには直接アクセスできませんので、 登録した tweener は削除できません。 工夫すれば削除と同じ意味のことを行うことはできます。

// アクセスできるオブジェクトを経由させて
// 削除のようなことを行う
var hoge = {};
hoge.tweener = function(prop, value){
  // 実際の tweener の処理はこっちに書く
  // ...
  return tween;
};

// 上の実質的な tweener を呼び出すだけの関数
var myTweener = function(prop, value){
  if( hoge.tweener ){
    return hoge.tweener.call(this, prop, value);
  }
  // 返り値が 無ければ次の tweener へと素通りする
};

// 登録は myTweener の方
jQuery.Animation.tweener( prop, myTweener );

// 削除は hoge.tweener を false になるような値にする
// そうすれば何もしない関数が実行されるだけになり、
// 次の tweener が呼び出されるようになる
hoge.tweener = false;
リストに戻る

目的値に"show","hide", "toggle"を指定した場合、 作成される tween オブジェクトがバージョンによって異なる

表題の通り、.animate()などに指定する目的値が "show","hide","toggle" であると、バージョンが 1.8.0 から 1.9.1 までと 2.0.0 では jQuery.Tweenオブジェクトが、 1.10.0 以降と 2.0.1 以降では tweener が作成したオブジェクトが tween オブジェクトとして使われます。

バージョンが 1.10.0 未満もしくは 2.0.1 未満だと、 独自の tweener を使い、且つ目的値に "show","hide","toggle" を指定した場合、tween オブジェクトが独自の tweener から 作成されずにjQuery.Tweenオブジェクトに なってしまいます。

その場合は目的値に "show","hide","toggle"を 使用するのをあきらめるかバージョンを上げましょう。

リストに戻る

使用例

おざなりですが独自 tweener と複数キューを使って、 同一プロパティに対する複数アニメーションの同時実行を 可能にしてみます。

まず tweener の定義と登録部分です。

/*
   独自 tweener の例:
     目的値が相対指定の場合だけ tween オブジェクトの 
     run メソッドを書き換えて、
     複数キューから同一プロパティへのアニメーションの
     平行実行を可能にする
*/

// デフォルト tweener を大幅に流用
// 「●」のあるところまでデフォルト tweener とほぼ同じ
var myTweener = function( prop, value ){

  var tween = this.createTween( prop, value ),
              // this は animation オブジェクト
              // animation の .createTween はその処理の中で
              // 作成した tween を animation.tweens に push する
      target = tween.cur(),
      core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
      rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
      parts = rfxnum.exec( value ),
              // → parts[1]:[-+], [2]:数値, [3]:単位
      unit = parts && parts[ 3 ] ||
             ( jQuery.cssNumber[ prop ] ? "" : "px" ),
      start = ( jQuery.cssNumber[ prop ] ||
                unit !== "px" && +target     ) &&
              rfxnum.exec( jQuery.css( tween.elem, prop ) ),
      scale = 1,
      maxIterations = 20;

  // 開始値を指定単位での値に変換している
  if ( start && start[ 3 ] !== unit ) {
    // Trust units reported by jQuery.css
    unit = unit || start[ 3 ];

    // Make sure we update the tween properties later on
    parts = parts || [];

    // Iteratively approximate from a nonzero starting point
    start = +target || 1;

    do {
      // If previous iteration zeroed out,
      // double until we get *something*
      // Use a string for doubling factor so we don't
      // accidentally see scale as unchanged below
      scale = scale || ".5";

      // Adjust and apply
      start = start / scale;
      jQuery.style( tween.elem, prop, start + unit );

      // Update scale, tolerating zero or NaN from tween.cur()
      // And breaking the loop if scale is unchanged or
      // perfect, or if we've just had enough
    } while ( scale !== (scale = tween.cur() / target) &&
              scale !== 1 &&
              --maxIterations );
  }

  // tween のプロパティを更新
  if ( parts ) {
    start = tween.start = +start || +target || 0;
    tween.unit = unit;
    // もし +=, -= が与えられていたら、相対アニメを行う
    // (※parts[1]には"+="や"-="ではなく"+","-"が入っている)
    tween.end = parts[ 1 ] ?
                start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
                +parts[ 2 ];

    // ●ここまでデフォルトとほぼ同じ処理
    // (変数のアクセス方法を調整しているだけ)

    // ここから追加
    // 相対指定ならば tween.run() を変える
    // これもデフォルトの tweet.run() を流用する
    if( parts[1] ){
      tween.before = 0;   // 前回の位置
      tween.movement = 0; // 前回から今回への移動量
      tween.distance = tween.end - tween.start;

      tween.run = function( percent ) {
        var eased,
            hooks = jQuery.Tween.propHooks[ this.prop ];

        // easing を適用して進行割合を算出
        if ( this.options.duration ) {
          this.pos = eased = jQuery.easing[ this.easing ](
            percent, this.options.duration * percent,
            0, 1, this.options.duration
            );
        } else {
          this.pos = eased = percent;
        }
        // now が開始値を原点とした現在位置
        // 小数だと終点がずれてしまうので整数値にする
        this.now = parseInt( this.distance * eased, 10 );
        // movement が今回移動する量
        this.movement = this.now - this.before;

        // step 関数実行
        if ( this.options.step ) {
          this.options.step.call( this.elem, this.now, this );
        }

        // before 更新
        this.before = this.now;
        // now を現在のプロパティの値からの相対値で更新
        this.now = this.cur() + this.movement;

        // hook(無ければデフォルトhook)で値を設定
        // now の値が設定される
        if ( hooks && hooks.set ) {
          hooks.set( this );
        } else {
          jQuery.Tween.propHooks._default.set( this );
        }

        return this;
      }
    }
  }

  return tween;
};


// tweener 登録
jQuery.Animation.tweener( myTweener ); // 全プロパティに対して登録

以下は上の tweener の利用例です。 2つのキューを使って、 同じプロパティへのアニメーションを2つ平行して実行しています。 デフォルトの tweener だとお互い自分の値を 現在値に関係無く指定してしまいうまく動きません。

// 上のオリジナル tweener の使用例
var element = document.getElementById("target");

// 現在アニメーション中か否かを調べる関数
// デフォルトキュー以外を利用する場合に使用する
var isAnimating = function(elem, queue){
  queue = queue || "fx";

  return jQuery.grep(jQuery.timers, function( timer ){
    return elem === timer.elem && queue === timer.queue;
  }).length > 0;
  // ※この関数では .delay() による停止中か否かまでは
  //   判断できません
};

// another キューでのアニメーション
var options = {};
options.queue = "another";
options.duration = 500;
var animating = isAnimating(element, "another");
$( element )
  .animate({ marginLeft: "+=100px" }, options )
  .animate({ marginLeft: "-=100px" }, options )
  .animate({ marginLeft: "+=100px" }, options )
  .animate({ marginLeft: "-=100px" }, options )
  .animate({ marginLeft: "+=100px" }, options )
  .animate({ marginLeft: "-=100px" }, options )
  .animate({ marginLeft: "+=100px" }, options )
  .animate({ marginLeft: "-=100px" }, options );

// デフォルトキュー以外は dequeue が必要な場合がある
if( !animating ){
  $( element ).dequeue("another");
}

// デフォルトキューでのアニメーション
delete options.queue;
options.duration = 2000;
$( element )
  .animate({ marginLeft: "+=200px" }, options )
  .animate({ marginLeft: "-=200px" }, options )

// marginLeft へのアニメーションを複数同時に実行できる
// (※相対指定のみの場合。一つでもそのプロパティに対して
//    目的値を絶対指定したアニメーションがあるとうまく動かない)

これはあくまで tweener 作成の雰囲気を伝えるために 例として適当に書いたものですので、 厳密にテストをしていません。ご注意下さい。

リストに戻る

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。