jQuery animate method hack : setInterval problem
jQuery には animate というメソッドがあります。
DOM 要素にマスクをかけつつのスライド機能など、いったいどういう処理で実現しているんだ?!といった表現を簡単に行うことが可能です。
しかし私はこのメソッドの利用はなるべく避けたいと考えていました。animate メソッドを実行すると、jQuery 処理内部でアニメーション描画用の setInterval が実行されてしまい、メインプロセスとは別のプロセスが発生してしまうからです。
複数のプロセスを制御するのはとても面倒で、何かにつけてコストが上がります。
なんとか jQuery 内部でプロセスが発生しないよう animate メソッドを制御する方法はないものかと、jQuery のソースを解読してみたところ、こちら意図通り利用できる方法が見つかりましたので以下に記述します。
jQuery version 1.7.1 で検証しています。
ソースコード
今回のサンプルでは、画面上に「test」と「test2」という文字が縦に並ぶようにした HTML と CSS を用意しました。
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="index.css" /> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="Index.js"></script> <script language="javascript"> $(function(){ new Index(); }); </script> </head> <body> <div id="box">test</div> <div id="box2">test2</div> </body> </html>
index.css
#box{ position: absolute; } #box2{ top: 50px; position: absolute; }
以下は jQuery の animate メソッドを利用する Index.js ファイルの内容です。
Index.js
function Index(){ this.init.apply(this, arguments); } Index.prototype.init = function(){ //start animation var box = $("#box").animate({"left": "+=500px"}, "slow"); var box2 = $("#box2").animate({"left": "+=500px"}, "slow"); //clone animation data this.timers = []; var tmpTimers = jQuery.timers; for(var i=0; i < tmpTimers.length; i++){ this.timers[i] = tmpTimers[i]; } //stop animation box.stop(); box2.stop(); //run animation var sc = this; this.mainFunction = this.run; this.timerId = setInterval(function(){ sc.mainFunction(); }, 10); }; Index.prototype.run = function(){ for(i = 0 ; i < this.timers.length; i++ ){ var timer = this.timers[i]; if(!timer() && this.timers[i] === timer) { this.timers.splice(i--, 1); } } if(this.timers.length == 0){ this.mainFunction = this.end; } }; Index.prototype.end = function(){ alert("end"); clearInterval(this.timerId); };
animate メソッドでアニメーションを実行したら、即 アニメーションデータの配列(jQuery.timers)のクローンを行い、アニメーションを停止しています。
後は任意のタイミングでアニメーション処理を実行してあげるだけです。今回のサンプルでは run メソッドで全てのアニメーションを再生しています。すべてのアニメーションが終了したら end アラートが表示されます。
サンプル
http://www.dango-itimi.com/blog/swf/152/
上記例では問題なさそうに動いていますが、まだあまり検証を行なっていないため多分穴は結構ありそうです。随時埋めていくという形で。
追記:穴1)
どの timer インスタンスがどの DOM 要素に対応しているのか判別できるようにしないと、用途が広がらないかもしれない。
追記:穴1解決)
timer.elem 変数経由で DOM 要素にアクセス可能な事が分かりました。