JavaScriptでのアニメーション処理に最適化したrequestAnimationFrameを使ってみた

ああけましておめでとうございます。年が明けて1週間以上経過しましたが、今年初のエントリーです。最近、ブログのネタが思い付かないし、書く気力も湧きません・・・。
ただ、今までは一週間に一度は書くということを目標につづけてきましたが(守れてない時も多いですが・・)、今年は書きたい時に書くという方針で行こうと思います。
そもそも、本で知った内容をブログにメモ代わりに書こうと思ったら、うまく再現できないということが多いんですよね・・・。「うまくいきませんでした」と書くのもなんなので、そういう時は試行錯誤したうえでブログに書いてません。

前置きはこのぐらいにして本題。

JavaScriptでアニメーションを行おうとした場合、ライブラリを使わないのであれば、setIntervalやsetTimeoutメソッドを使うことになると思いますが、最近のブラウザではrequestAnimationFrameという名前のとおりアニメーションに最適化した(といっても、アニメーション以外にも使えますが)メソッドがあると知って試しに使ってみました。

簡単に説明すると60fpsの間隔で引数のコールバック関数を実行するメソッドとなります。setTimeoutの第二引数を1000/60とした時とほぼ同じですが、requestAnimationFrameはアクティブでないタブやウィンドウでは処理しなかったり、描画処理の準備が整っている場合にだけ実行するなど、アニメーションに最適化した処理を行うことができるメソッドとなっているようです。なお、60fpsと書きましたが、実際にはコールバック関数内でrequestAnimationFrameを呼ばなければ次の処理は実行されません(つまり、動き的にはsetIntervalではなく、setTimeoutと同じ)。なので、正確には1/60秒後に引数のコールバック関数を実行するメソッドといったほうが正しいかもしれません。
参考:window.requestAnimationFrame – Web API インターフェイス | MDN

というわけで、実際に使ってみました。内容は、直径100pxの赤い丸が1辺の長さが500pxの四角いコンテナ内を右下左上の順番に5pxずつ動くようなアニメーションです。

HTML

CSS
#container{
	position:fixed;
	width:500px;
	height:500px;
	border:1px solid black;
	top:0;
	left:0;
	right:0;
	bottom:0;
	margin:auto;
}
#animation-box{
	position:absolute;
	width:100px;
	height:100px;
	left:0;
	top:0;
	background-color:red;
	border-radius:50%;
}

JavaScript

$(function(){
	var flgTurn = 0; //0:右、1:下、2:左、3:上
	var left=0, top=0;
	var $animationBox = $('#animation-box');
	window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
							window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
	
							
	function animationLoop(){
	
		switch(flgTurn){
			case 0: //右方向
				left += 5;
				$animationBox.css('left', left);
				if(left===400){
					flgTurn = 1;
				}
				break;
			case 1: //下方向
				top += 5;
				$animationBox.css('top', top);
				if(top===400){
					flgTurn = 2;
				}
				break;
			case 2: // 左方向
				left -= 5;
				$animationBox.css('left', left);
				if(left===0){
					flgTurn = 3;
				}
				break;
			case 3: // 上方向
				top -= 5;
				$animationBox.css('top', top);
				if(top===0){
					flgTurn = 0;
				}
				break;
		}
		
		requestAnimationFrame(animationLoop);
	}
	animationLoop();
});

実行結果:requestAnimationFrameのテスト

それなりにスムーズに赤丸が動いていると思います。
また、同じことを、setTimeoutに変更して試してみました。『requestAnimationFrame(animationLoop);』の箇所を『setTimeout(animationLoop, 1000/60);』に変えただけです。
実行結果:setTimeoutのテスト

違いはほとんどないと思います。試しに、タブを切り替えてから再度見てみると、requestAnimationFrameのほうはタブを切り替える前の位置から始まり(非表示の時は止まっている)、setTimeoutのほうは表示していない時は別の位置から始まる(非表示の時も動いている)という環境もあるかもしれません(自分の環境では、IE11では確かにそのような動きになったのですが、ChromeやFirefoxではどちらもタブ切り替え時の位置から始まりました)。

ただ、いろいろと問題もあるらしく、例えばsetIntervalとは併用して使えないという問題があるようです(参考:jQueryで破棄されたrequestAnimationFrameとJSでのアニメーション実装で注意すること)。なので、使いどころを考えて使ったほうがよさそうです。

コメント

タイトルとURLをコピーしました