基于setTimeout的Javascript时间间隔动画引擎
之前写过一个基于setInterval定时执行的动画引擎,在实际的应用当中,经过一些小的修改,感觉还不错。
也许是由于对jq作者的好感(抱歉我一直己不得他的名字,亏我还有他一本书……),在他书中的一段动画代码一直让我念念不忘。大概是下面这个样子:
someBox.style.left = arg + "px";
someBox.style.top = arg + "px";
}
for(var i = 0; i < 100; i++){
setTimeout((function(pos){
return function(){ someAnimateMethod(pos); }
})(i), i * 100);
}
至于内存释放或者clearTimeout的问题先不说,这种方式,对IE尤其有效,尤其是IE6,效率极高。反倒FF非常郁闷,不但时间间隔不匀,而且同等设置下效率很低。
经过实际测试,除了IE之外(包括IE6/7/8),使用setInterval的效率都非常不错,尤其是chrome。所以作为开发类库而言,我首选了使用setInterval来实现动画引擎。但是正如开头所说的,这个n多setTimeout的方式,已经变成了一个纠结。终于今天,熬到现在,基本完成了雏形。代码并不多:
* AnimateEngine [setTimeout base] v0.1
* by MK in MKing's Kingdom
* [Created by Aptana Studio 3]
* [Testing passed under FireFox3.5.9 / GNOME / openSuSE11.2]
* 2010-6-11
* *****************
* It's free code. If you want, you can delete all the words above. Have funs.
* *****************
*/
AnimateEngine = {
fps : 30, seed : 0, timer : [], groups : {}, handlerGroup : null, length : 0,//handlerFrame : -1,
push : function(f, args){
var len = args.length
, idx = 0, name = '$' + this.seed++
, _this = this
;
this.groups[name] = [];
do {
if(!this.timer[idx]){ this.timer.push(this.newFrame(idx)); }
this.groups[name].push({ a : args[idx], i : idx, n : name, f : f});
} while(++idx < len);
len = idx = name = null;
},
newFrame : function(index){
var _this = this;
return setTimeout(function(){
var p, o;
for(p in _this.groups)
if(p != _this.handlerGroup)
if(!!(o = _this.groups[p].shift())) o.f(o.a + o.n);
else delete _this.groups[p];
else _this.timer.push(_this.newFrame(_this.timer.length))
p = o = null;
clearTimeout(_this.timer.shift());
}, index * parseInt(1000 / this.fps));
}
}
基本的思路和原来基于setInterval的引擎是一样的,就是将当前页面看作一个动画窗口,合并所有并行的动画帧到一条时间线上,以防止众多setTimeout同时执行时抢占资源导致阻塞。
很多使用setTimeout来实现动画时间间隔的代码,基本都是用嵌套的方式,类似于:function run(){ do something; setTimetout(fun, 100); }的方式。如果这样的话,实现起来会非常容易,但这样其实不如直接用setInterval。
而像这种,直接将所有动作写到时间线上的方式,固然效率要高一些,但是写时间线的工作是动态的,维护起来比较困难。首先需要分解动画的所有帧,而不同帧数的动画添加进来时,又需要动态扩展帧数;头部的帧执行结束后,又需要移除。在实际运行当中,可能某个动画——比如横向滚动在响应用户鼠标事件时——可能需要暂停,该如何剔除这个暂停的动画而不影响整个时间线进程?然后怎么恢复?怎么“回收”过期的资源?
以上的这些问题基本解决了。全局时间线暂停和恢复的方法还没实现。太晚了,累了。附上完整代码。
2 "http://www.w3.org/TR/html4/loose.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6 <title>New Web Project</title>
7 </head>
8 <body>
9 <h1></h1>
10 <h3></h3>
11 <h4></h4>
12 </body>
13 <script type="text/javascript">
14 /*
15 * AnimateEngine [setTimeout base] v0.1
16 * by MK in MKing's Kingdom
17 * [Created by Aptana Studio 3]
18 * [Tesing passed under FireFox3.5.9 / GNOME / openSuSE11.2]
19 * 2010-6-11
20 * *****************
21 * It's free code. If you want, you can delete all the words above. Have funs.
22 * *****************
23 */
24 AnimateEngine = {
25 fps : 30, seed : 0, timer : [], groups : {}, handlerGroup : null, length : 0,//handlerFrame : -1,
26 push : function(f, args){
27 var len = args.length
28 , idx = 0, name = '$' + this.seed++
29 , _this = this
30 ;
31 this.groups[name] = [];
32 do {
33 if(!this.timer[idx]){ this.timer.push(this.newFrame(idx)); }
34 this.groups[name].push({ a : args[idx], i : idx, n : name, f : f});
35 } while(++idx < len);
36 len = idx = name = null;
37 },
38 newFrame : function(index){
39 var _this = this;
40 return setTimeout(function(){
41 var p, o;
42 for(p in _this.groups)
43 if(p != _this.handlerGroup)
44 if(!!(o = _this.groups[p].shift())) o.f(o.a + o.n);
45 else delete _this.groups[p];
46 else _this.timer.push(_this.newFrame(_this.timer.length))
47 p = o = null;
48 clearTimeout(_this.timer.shift());
49 }, index * parseInt(1000 / this.fps));
50 }
51 }
52 var h1 = document.getElementsByTagName('h1')[0]
53 , h3 = document.getElementsByTagName('h3')[0]
54 , h4 = document.getElementsByTagName('h4')[0]
55
56 var args = ['1','2','3','4','1','2','3','4','5','6','7','8','a','9','-'];
57 var args2 = ['a','b','c','-'];
58 var args3 = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','-'];
59 var handler = function(arg){ h1.innerHTML += arg + ','; };
60 var handler2 = function(arg){ h3.innerHTML += arg + ','; };
61 var handler3 = function(arg){ h4.innerHTML += arg + ','; };
62 AnimateEngine.push(handler3, args3);
63 AnimateEngine.push(handler2, args2);
64
65 setTimeout(function(){
66 AnimateEngine.handlerGroup = '$0';
67 AnimateEngine.push(handler, args);
68 }, 160)
69 setTimeout(function(){ AnimateEngine.handlerGroup = '$2' }, 200)
70 setTimeout(function(){ AnimateEngine.handlerGroup = null }, 300)
71 </script>
72 </html>
73
下面是基于setInterva动画引擎的一个早期版本。
http://www.cnblogs.com/muse/articles/1711505.html