Menu

一個JS動畫框架 - 基於 HTML5 Canvas

好久之前,發現必須應該有個動畫框架,才能支持開發出更加複雜的動畫效果。所以在學習Canvas的過程中,我就萌發了這樣的念頭,在無數大神大牛的啟發下,今天“出廠”一個小的動畫框架,希望能和有興趣的朋友一起探討交流一下。

 

 

1、實現精靈

模仿Flash的動畫裡面有幀和精靈(sprite)的概念,由於canvas只是一個元素,並沒有相應的這些東西。所以在這裡,我參照別人的思想,實現一個精靈父類,讓所有的精靈對象都繼承它。代碼如下:

/*
**精靈父類
*/
var Sprite=function(){};
Sprite.prototype={
        /*
        **每個精靈對象自己的draw函數
        */
        draw:function(){
        },
        /*
        **添加運動方式
        */
        addMotionWay:function(fun){
                this.motionFunc.push(fun);
        },
        /*
        **運動計算
        */
        countMotionFun:function(stepTime){
                for(var i=this.motionFunc.length;i--; ){
                        this.motionFunc[i].call(this,stepTime/100);
                }
                this.draw();
        }
}


 

2、實現幀

在實現了精靈後,所以我就在前人的代碼上,增加一些自己的東西,實現一個Canvas類,實現JS的“幀”。代碼如下: 之所以用setTimeout而不用setInterval,原因是每一幀的計算時間如果定死了,當動畫中的精靈比較多的時候,相應的運動計算量比較大,如果進入下一幀,相應的計算卻還沒計算好,將出現跳幀。所以,我採用setTimeout,將每一幀的計算完成後,才進行下一幀。

/*
** Canvas類 管理所有動畫實體sprites
*/
var Canvas=function(id){
        //關聯canvas元素
        this.canvas=document.getElementById(id);
        //精靈字典
        this.sprites={};
        //當前幀數
        this.frame;
        this.frameNum;
        //所關聯canvas元素上下文對象
        this.ctx=this.canvas.getContext('2d');
        //幀長
        this.stepTime;
        //觸發器
        this.sT;
}

Canvas.prototype={
        /*
        **初始化
        */
        init:function(){
                this.fps=0;
                //記錄最後一幀的時間
                this.lastFrameTime=(new Date()).getTime();
                this.frame=0;//當前幀數歸0
                this.frameNum=30;//每30幀計算一次fps
                this.stepTime=1;//和fps成反比
        },
        /*
        **開始
        */
        begin:function(){
                this.sT=setTimeout((function(param){
                        return function(){ param.render();}
                })(this),this.stepTime);
        },
        /*
        **渲染,
        */
        render:function(){
                //計算fps,參考oldj http://oldj.net/
                this.frame++;
                if(this.frame % this.frameNum == 0){
                        //
                        t = (new Date()).getTime();
                        this.fps= Math.round((this.frameNum*10000)/ (t - this.lastFrameTime)) / 10;
                        this.lastFrameTime = t;
                        showFps(this.fps);
                        // 動態調整 step_time ,保證 fps 恆定
                        if (this.fps < 29.6 && this.stepTime > 10) {
                                this.stepTime--;
                        } else if (this.fps > 30.4) {
                                this.stepTime++;
                        }
                }
                this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
                //調用每個精靈的運動方程
                for(var i in this.sprites){
                        if(typeof(this.sprites[i])=="function") continue;
                        this.sprites[i].countMotionFun(this.stepTime);
        }
                this.sT=setTimeout((function(param){
                        return function(){ param.render();}
                })(this),this.stepTime);
        },
        /*
        **添加動畫元素
        */
        addSprite:function(name,sprite){
                this.sprites[name]=sprite;
        },
        /*
        **刪除動畫元素
        */
        removeSprite:function(name){
                if(this.sprites[name]){
                        delete this.sprites[name];
                }
        },
        /*
        **停止動畫
        */
        stop:function(){
            clearInterval(this.sT)
    },
        clear:function(){
                for(var i in this.sprites){
                        if(typeof(this.sprites[i])=="function") continue;
                                delete this.sprites[i];
                }
        }
}

3、關於運動

原來我一直在糾結這個問題,不知道如何計算運動,我的意思是,運動的形式各種各樣,要封裝起來不是那麼簡單,後來看了Milo大神關於運動學的模擬,頓時貌似頓開,運用了歐拉方法,使用二維的矢量類來表示一切速度,加速度,位置。

4、運動的疊加

一個元素的運動,不可能只是單一一種運動,所以在框架中,我使用了一個數組:motionFunc,來保存每個元素自己特有的運動方程,並且可以疊加,在最後的例子可以看到,一個直線運動疊加往返運動。

5、實現FPS

FPS,哦,這個我知道,第一人稱射擊遊戲嘛,我的最愛-_-|||。
FPS:Frames Per Second 每秒傳輸幀數。也就是刷新頻率。玩過3D遊戲都知道,FPS越高,遊戲越流暢。
FPS值=每秒/每幀用過的時間。
最好取平均值計算比較準確。
感謝oldj大哥對我的指導。這裡計算FPS的思路和代碼參考oldj大哥

好了,一個小小的框架就完成了,下面看看效果。按add增加小球

{jumi [examples/fun2d/testFun2D.html]}

詳細的相關代碼
參考資料:
Canvas教程
HTML5嘗鮮
用JavaScript玩轉遊戲物理(一)運動學模擬與粒子系統