基于setTimeout的Javascript时间间隔动画引擎

之前写过一个基于setInterval定时执行的动画引擎,在实际的应用当中,经过一些小的修改,感觉还不错。

也许是由于对jq作者的好感(抱歉我一直己不得他的名字,亏我还有他一本书……),在他书中的一段动画代码一直让我念念不忘。大概是下面这个样子:

N多setTimeout
function someAnimateMethod(arg){
    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的方式,已经变成了一个纠结。终于今天,熬到现在,基本完成了雏形。代码并不多:

基于setTimeout的Javascript动画引擎
/*
 * 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。

而像这种,直接将所有动作写到时间线上的方式,固然效率要高一些,但是写时间线的工作是动态的,维护起来比较困难。首先需要分解动画的所有帧,而不同帧数的动画添加进来时,又需要动态扩展帧数;头部的帧执行结束后,又需要移除。在实际运行当中,可能某个动画——比如横向滚动在响应用户鼠标事件时——可能需要暂停,该如何剔除这个暂停的动画而不影响整个时间线进程?然后怎么恢复?怎么“回收”过期的资源?

 以上的这些问题基本解决了。全局时间线暂停和恢复的方法还没实现。太晚了,累了。附上完整代码。

 

完整的代码
 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 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 

 

posted @ 2010-06-11 05:05  MKing's Kindom  阅读(450)  评论(0编辑  收藏  举报