独自の 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 作成の雰囲気を伝えるために 例として適当に書いたものですので、 厳密にテストをしていません。ご注意下さい。
リストに戻る