1. 程式人生 > >HTML5 Canvas 逐幀動畫的實現

HTML5 Canvas 逐幀動畫的實現

和C++遊戲開發相同,HTML5逐幀動畫需要的影象元素也是一張繪製了每一幀影象效果的圖片。通過迴圈繪製各幀的影象來實現動畫的效果。

本示例中演示的是一個小人,預設狀態下,小人朝右方站立;按下左/右方向鍵的時候,小人朝左/右方奔跑(在畫布中沒有位移);鬆開按鍵後保持奔跑的方向站立。

其中,向左或向右站立分別是一張6幀的圖片,向左或向右奔跑分別是一張12幀的圖片。

程式碼如下:

HTML程式碼:

  1. <canvasid="canvas"width="600"height="400">
  2.     <p>Your browser does not support the canvas element!
    </p>
  3. </canvas>
JavaScript程式碼如下:

以下這段程式碼已經在本人的博文中多次重用,所以就不解釋了。

[javascript] view plain copy
  1. Array.prototype.remove = function(obj) {  
  2.     for (i inthis) {  
  3.         if (this[i] === obj) {  
  4.             this.splice(i, 1);  
  5.         }  
  6.     }  
  7. }  
  8. function BasicObject(x, y, order) {  
  9.     this.x = x;  
  10.     this.y = y;  
  11.     this.order = isNaN(order) ? 0 : order;  
  12.     this.addTo = function(list) {  
  13.         list.push(this);  
  14.         list.sort(function(a, b) {return a.order - b.order;});  
  15.     }  
  16.     this.removeFrom = function(list) {  
  17.         list.remove(this);  
  18.     }  
  19. }  
逐幀動畫的基礎物件,繼承自基礎物件類,添加了影象、總幀數兩個屬性,以及繪製逐幀物件的方法
[javascript] view plain copy
  1. function FrameAnimationObject(x, y, order, image, frame) {  
  2.     BasicObject.call(this, x, y, order);  
  3.     this.image = image;  
  4.     this.frame = frame;  
  5.     this.currentFrame = 0;  
  6.     this.draw = function(context) {  
  7.         var sw = this.image.width / this.frame;  
  8.         var sx = this.currentFrame * sw;  
  9.         context.drawImage(this.image, sx, 0, sw, this.image.height, this.x, this.y, sw, this.image.height);  
  10.         this.currentFrame++;  
  11.         this.currentFrame = (this.currentFrame >= this.frame) ? 0 : this.currentFrame;  
  12.     }  
  13. }  
  14. FrameAnimationObject.prototype = new BasicObject();  

奔跑小人的類,繼承自逐幀動畫基礎物件,指定了需求使用的影象資源,並添加了響應鍵盤事件的方法

[javascript] view plain copy
  1. function Person(x, y, order) {  
  2.     FrameAnimationObject.call(this, x, y, order);  
  3.     this.image = new Image();  
  4.     this.image.src = "stop_right.png"
  5.     this.frame = 6;  
  6.     this.onkeydown = function(event) {    
  7.         if (event.keyCode == 37) {    
  8.             this.image.src = "run_left.png";    
  9.             this.frame = 12;    
  10.         }    
  11.         elseif (event.keyCode == 39) {    
  12.             this.image.src = "run_right.png";  
  13.             this.frame = 12;      
  14.         }    
  15.         this.currentFrame = (this.currentFrame >= this.frame) ? 0 : this.currentFrame;    
  16.     }    
  17.     this.onkeyup = function(event) {    
  18.         if (event.keyCode == 37) {    
  19.             this.image.src = "stop_left.png";    
  20.         }    
  21.         elseif (event.keyCode == 39) {    
  22.             this.image.src = "stop_right.png";    
  23.         }    
  24.         this.frame = 6;    
  25.         this.currentFrame = (this.currentFrame >= this.frame) ? 0 : this.currentFrame;    
  26.     }    
  27. }  
  28. Person.prototype = new FrameAnimationObject();  

動畫引擎類以及程式入口

[javascript] view plain copy
  1. function Engin() {  
  2.     var canvas = document.getElementById("canvas");  
  3.     var context = canvas.getContext("2d");  
  4.     var buffer = document.createElement("canvas");  
  5.     buffer.width = canvas.width;  
  6.     buffer.height = canvas.height;  
  7.     var bufferCtx = buffer.getContext("2d");  
  8.     var objs = new Array();  
  9.     const FPS = 20;  
  10.     this.manage = function() {  
  11.         bufferCtx.clearRect(0, 0, buffer.width, buffer.height);  
  12.         context.clearRect(0, 0, canvas.width, canvas.height);  
  13.         for (x in objs) {  
  14.             if (objs[x].update) {  
  15.                 objs[x].update(objs);  
  16.             }  
  17.         }  
  18.         for (x in objs) {  
  19.             if (objs[x].draw) {  
  20.                 objs[x].draw(bufferCtx);  
  21.             }  
  22.         }  
  23.         context.drawImage(buffer, 0, 0);  
  24.     }  
  25.     document.onkeydown = function(event) {  
  26.         for (x in objs) {  
  27.             if (objs[x].onkeydown) {  
  28.                 objs[x].onkeydown(event);  
  29.             }  
  30.         }  
  31.     }  
  32.     document.onkeyup = function(event) {  
  33.         for (x in objs) {  
  34.             if (objs[x].onkeyup) {  
  35.                 objs[x].onkeyup(event);  
  36.             }  
  37.         }  
  38.     }  
  39.     this.run = function() {  
  40.         var p = new Person(canvas.width / 2, canvas.height / 2);  
  41.         p.addTo(objs);  
  42.         setInterval(this.manage, 1000 / FPS);  
  43.     }  
  44. }  
  45. window.onload = function() {  
  46.     new Engin().run();  
  47. }  

需要說明的是,本次將鍵盤事件的響應放到了動畫物件的類中來實現,並在引擎類中通過設定document的鍵盤事件來引用,這昂做事為了依照上一篇博文總所說的將動畫物件的邏輯操作封裝在最外層,同時避免了引擎類的過分膨脹。當動畫物件逐漸增多時,效果更加明顯。