「プログラミング経験はそこそこだけど、JavaScriptはあんまり」な人向け社内勉強会資料vol.5
Shogo Ohta, 2009-08-24JavaScriptでアニメーションを実現する方法を紹介します。
まずは失敗ケースです。
var box = document.getElementById('box');
for (var i = 0; i < 100; i++){
box.style.left = i + '%';
}
このコードはboxという要素のleftの値を1%ずつ増加させています。一見するとこれで左から右にアニメーションできそうに思えますが、実際には最後の100%の位置しか表示されません。これはブラウザがスタイルの変更を反映(再描画)するタイミングがスクリプトの実行後にしかないためです。
つまり、アニメーションするためには、再描画を行わせる必要があります。これにはsetTimeout、setIntervalというタイマー関数を使用して処理を分断することで実現します。
var box = document.getElementById('box');
var i = 0;
var timer = setInterval(function(){
box.style.left = i++ + '%';
if (i > 100) {
clearInterval(timer);
}
},10);
これでアニメーションを実現できました。しかし、この方法では必ず100回再描画が行われるため、仮に1回の再描画に1秒かかる環境では100秒、10msで再描画できる場合、1秒でアニメーションすることになってしまいます。無駄に長いアニメーションはユーザーにストレスを与えるので、このままでは使用できません。
そこで、何回再描画するか、という指定方法から、何秒間アニメーションするか、という指定方法に切り替えます。ハイスペックな環境ではそれに応じて多くの再描画が行われ、ロースペックな環境では少ない再描画で処理を済ませ、全体の実行時間を同じにするという方法です。
var box = document.getElementById('box');
var begin = new Date()*1;/* Dateオブジェクトは数値に型変換するとシリアル値を返す */
var timer = setInterval(function(){
var now = new Date()*1;
var progress = (now-begin) / 10;/* (now-begin) / 1000 * 100 */
box.style.left = progress + '%';
if (progress > 100) {
clearInterval(timer);
box.style.left = '100%';
}
},10);
上述のサンプルは単純な等速直線運動でしたが、簡単にバリエーションを追加できます。
「経過時間、初期値、変動値、継続時間」の4つから、現在値を求めるEasing関数が広く使われています。
var box = document.getElementById('box');
var begin = new Date()*1;
var from = 0;
var to = 100;
var distance = to - from;
var duration = 1000;
var timer = setInterval(function(){
var now = new Date()*1;
var time = now-begin;
var progress = distance * time / duration + from;
box.style.left = progress + '%';
if (time > duration) {
clearInterval(timer);
box.style.left = to + '%';
}
},10);
Easing関数は自作することもできますが、Tweenerに搭載されている関数はhttp://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.htmlにまとめられています。
JavaScriptでは、JSTweeenerにこれらの関数群が移植されているので、これを利用できます。
var easing = JSTweener.easingFunctions.easeOutBounce;
var box = document.getElementById('box');
var begin = new Date()*1;
var from = 0;
var to = 100;
var distance = to - from;
var duration = 1000;
var timer = setInterval(function(){
var now = new Date()*1;
var time = now-begin;
var progress = easing(time, from, distance, duration);
box.style.left = progress + '%';
if (time > duration) {
clearInterval(timer);
box.style.left = to + '%';
}
},10);