こんにちは、
アニメーションの前提知識
HTMLにおける通常のアニメーションを構成するのは、
特定の要素と、
まず、
var y = 0;
var element = document.getElementById('cbjs-17-1');
var id = setInterval(function(){
y++;
element.style.top = y/10 + 'px';
if (y === 1000){
clearInterval(id);
}
}, 1); // 1ms置きに実行
単純計算で言えば、
そこで、
つまり、
var begin = new Date() - 0;
var element = document.getElementById('cbjs-17-2');
var id = setInterval(function(){
var current = new Date() - begin;
if (current > 1000){
clearInterval(id);
current = 1000; // 1000以上になっているので、調整する
}
element.style.top = current / 10 + 'px';
}, 10); // 10ms置きに実行
この方法ではハイスペックな環境では滑らかなアニメーションに、
アニメーションの汎用化とEasing関数
さて、
Easing関数とは、
function easing(time, from, distance, duration){
return distance * time / duration + from;
}
var begin = new Date() - 0;
var element = document.getElementById('cbjs-17-3');
var from = 0; // 初期値
var distance = 100; // 変動値
var duration = 1000; // 継続時間
var id = setInterval(function(){
var time = new Date() - begin; // 経過時間
var current = easing(time, from, distance, duration);
if (time > duration){
clearInterval(id);
current = from + distance;
}
element.style.top = current + 'px';
}, 10); // 10ms置きに実行
easing関数は自作もできますが、
JavaScriptから扱う場合はTweenerのJavaScriptポートである、
var easeOutBounce = JSTweener.easingFunctions.easeOutBounce;
var begin = new Date() - 0;
var element = document.getElementById('cbjs-17-3');
var from = 0; // 初期値
var distance = 100; // 変動値
var duration = 1000; // 継続時間
var id = setInterval(function(){
var time = new Date() - begin; // 経過時間
var current = easeOutBounce(time, from, distance, duration);
if (time > duration){
clearInterval(id);
current = from + distance;
}
element.style.top = current + 'px';
}, 10); // 10ms置きに実行
アニメーションのライブラリ化
さて、
ライブラリ化にあたって、
/**
* アニメーション関数
* @param target 対象オブジェクト(nodeのstyleなど)
* @param properties プロパティの定義
* @param options アニメーションのオプション(省略可)
*/
function MiniTweener(target, properties, options){
// 入れ物となるオブジェクトを用意
var item = {};
item.target = target;
item.properties = properties;
item.options = options || {};
// プロパティの入れ物も用意
item.props = [];
for (var name in properties){
// 各プロパティをオブジェクトにまとめ直して配列に挿入
// プロパティごとに初期値と最終値、接頭辞、接尾辞を持つ
var prop = properties[name];
item.props.push({
name :name,
from :prop.from,
to :prop.to,
distance:prop.to - prop.from,
prefix :prop.prefix || '',
suffix :prop.suffix || 'px'
});
}
// アニメーションのオプション定義
item.duration = item.options.duration || 1000;
item.easing = item.options.easing || MiniTweener.easing;
item.onComplete = item.options.onComplete;
// 開始時刻
item.begin = new Date() - 0;
// アニメーションの定義セットを配列に入れる
MiniTweener.Items.push(item);
if (!MiniTweener.timerId){
// アニメーションが開始していなければ開始
MiniTweener.start();
}
return item;
}
MiniTweener.easing = function(time, from, dist, duration){
return dist * time / duration + from;
};
MiniTweener.FPS = 60;
MiniTweener.Items = [];
MiniTweener.loop = function(){
var items = MiniTweener.Items;
var now = new Date() - 0; // 現在時刻
var time;
var n = items.length;
// アニメーションが終了した時に定義を配列から削除するので、
// 削除しても添え字に影響が出ないように配列を後ろから走査する
while (n--){
var item = items[n];
// 経過時刻
time = now - item.begin;
if (time < item.duration){
// 各プロパティの途中経過を求め、反映
for(var i = 0; i < item.props.length;i++){
var prop = item.props[i];
var current = item.easing(time, prop.from,
prop.distance, item.duration);
item.target[prop.name] = prop.prefix +
current + prop.suffix;
}
} else {
// 最終的な値を設定
for(var i = 0; i < item.props.length;i++){
var prop = item.props[i];
item.target[prop.name] = prop.prefix +
prop.to + prop.suffix;
}
// 終了後のコールバック
if (item.onComplete){
item.onComplete();
}
// 配列から削除
items.splice(n, 1);
}
}
if (!items.length){
// アニメーション定義が空になっていたら停止
MiniTweener.end();
}
};
MiniTweener.start = function(){
// TimerのIDを格納しておく
MiniTweener.timerId = setInterval(MiniTweener.loop,
1000 / MiniTweener.FPS);
};
MiniTweener.end = function(){
MiniTweener.Items = [];
// タイマーを停止
clearInterval(MiniTweener.timerId);
delete MiniTweener.timerId;
};
少々長いですが、
なお、
この関数を使うときは次のように呼び出します。
MiniTweener(element.style, {
top :{from:100, to:200},
left :{from:50 , to:100, suffix:'%'},
},{
duration: 500,
});
第一引数に操作対象の要素のstyleオブジェクトを渡し、
最後に簡単なアニメーション・
var node = document.getElementById('cbjs-17-5');
var timerId = setInterval(function(){
var i = node.cloneNode(true);
// ランダムな文字を生成
var s = Math.floor(Math.random()*36).toString(36);
i.appendChild(document.createTextNode(s));
node.parentNode.appendChild(i);
// アニメーション適用
MiniTweener(i.style, {
top :{from:100,to:200*Math.random()},
left :{from:50 ,to:100*Math.random(), suffix:'%'},
fontSize:{from:50 ,to:300*Math.random(), suffix:'%'}
}, {
duration: Math.random()*1000,
onComplete:function(){
// アニメーションが終わった要素を削除
node.parentNode.removeChild(i);
}
});
}, 10);
まとめ
今回はアニメーションの基礎とそれをベースにライブラリ化を行ってみました。次回はアニメーションの実用的なケースとCSS3に関係した部分について解説します。