一個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玩轉遊戲物理(一)運動學模擬與粒子系統