posts - 48,comments - 156,trackbacks - 0

          一、為什么選擇 HTML5

          HTML5 邊玩邊學算上這篇已經是第七篇了,在這篇開始之前,我想先說明一下為什么叫“HTML5” 邊玩邊學,因為有人對 HTML5 提出質疑,畢竟他是一個新生事物。我承認我用 HTML5 來吸引眼球了,如果看過邊玩邊學系列的每一篇,你會發現前六篇文章內容的和 HTML5 關系不是太大,真正的內容其實是 2D 圖形圖像編程的學習筆記。

          如果我們想學習 2D 編程,其實可供選擇的編程環境數不勝數:MFC、Delphi都有圖形圖像處理功能(即GDI),Java、.Net 更不用說了,如果你支持開源,GTK、QT、wxPython 也是不錯的選擇,Flash 更是拿手好戲,甚至幾個流行的只能手機平臺應該也有 2D 模塊。

          如果你選擇了上面任何一款編程環境學習 2D 編程,你會發現他們的內容基本上是一樣的:線型、填充、顏色、漸變、圖像、組合、裁剪區、變形等等,甚至連函數名很多都是一摸一樣,畢竟他們的理論基礎都是圖形學。

           搞清楚我們真正想學習什么以后,其實編程環境只是個工具而已,我們根據個人喜好,選擇最方便的一款來使用。其實我更青睞 Python 編程環境,只是如果我用了Python,估計跟我交流的人就不會太多了,大家機器里安裝 Python 運行時的估計不會太多。

          那么為什么選擇 HTML5 而不是其他呢?首先,Javascript 語法簡潔靈活,相應的函數庫小巧但是夠用, HTML5 Canvas 標簽的 2D 表現能力也達到了要求,Chrome 瀏覽器的運行速度讓人滿意。除此之外,我們不用安裝笨重的集成開發環境,不需要安裝運行時,我們只需要一個加強功能的記事本、一個瀏覽器就可以去實踐我們的想法,并且直接將效果呈現在網絡上。我們只是發表文章同其他人分享自己的想法而已,至于平臺、框架、語言特性,這些無關的東西當然牽扯的越少越好,這就是我選擇 HTML5 的原因。

          所以,請大家不要誤解了標題的含義:這個系列并不是 HTML5 的學習筆記,而只是用 HTML5 來展現一些知識內容而已,你更多關注的應該是知識和內容本身,你可以在任何其他一款編程環境下再現他們。

           

          二、動畫初步

          動畫就是一系列連續的畫面按順序呈現出來而已,只是,在電影電視中,這些畫面實現已經被準備好了,而在電腦程序中,我們見到每一瞬間的畫面都是即時繪制的,大體流程可以表述如下:

          a、輕微改變圖形的數據(坐標、形狀、顏色等等)

          b、清空畫布

          c、繪制圖形

          d、回到步驟 a

          當然,這里只是給出了一個最簡單的流程框架,要實現復雜的動畫可能還要考慮更多的問題,比如局部清除、碰撞檢測之類的。

           

          另外,繪制過程中有兩個速度需要控制:

          第一個是繪制速度,即每秒鐘繪制多少次(幀),或者也可以這樣說,每一幀暫停多少時間。如果你的動畫每一幀都是一個樣子,只是位置不同,這個速度影響不大。

          第二個圖形移動的速度。

          所以,千萬不要把這兩個速度搞混了,繪制的越快,只能代表動畫更流暢,但并不代表你的圖像移動的更快。

           

          使用 HTML5 繪制動畫基本上就是上面這個流程,只是你還需要注意兩點:

          1、為了方便繪制的圖形,我們經常會改變上下文對象的的狀態,所以在繪制圖形前后,千萬別忘了保存和恢復狀態,如果你不太了解狀態是什么,請看前面的一篇文章《HTML5邊玩邊學(6):汽車人,變形......》

          2、我們需要將整個繪制動作放到定時器里面,否則整個瀏覽器將失去響應。Javascript 有兩個定時器方法,分別是:

          setInterval(code,millisec) 和 setTimeout(code,millisec)

          這兩個方法我就介紹了,可以去 Google 相關的資料。

          下面我們給出一個上下移動方塊的小動畫,當遇到頂部或者底部時,會改變方向。代碼如下:

          基本動畫
          <canvas id="canvas1" width="250" height="300" style="background-color:black">
              你的瀏覽器不支持 Canvas 標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          </canvas><br/>
          幀數:
          <input  id="txt1" type="text" value="25"/><br/>
          每次移動距離:
          <input type="text" id="txt2" value="10"/><br/>
          <input type="button" value="開始" onclick="move_box()"/>
          <input type="button" value="暫停" onclick="stop()"/>


          <script type="text/javascript">
              
          //定時器
              var interval=null;
              
              
          //停止動畫
              function stop(){
                  clearInterval(interval);
              }

              
          //===================================================================
              
          //基本動畫
              
          //====================================================================
              function move_box(){
                  
          //停止動畫
                  stop();
                  
          //移動速度
                  var delta=parseInt(document.getElementById('txt1').value);
                  
          //每秒繪制多少次
                  var fps=parseInt(document.getElementById('txt2').value);

                  
          //畫布對象
                  var canvas=document.getElementById("canvas1")
                  
          //獲取上下文對象
                  var ctx = canvas.getContext("2d");
                  
          //設置顏色
                  ctx.fillStyle="red";
                  
                  
          //方塊的初始位置
                  var x=100;var y=50;
                  
          //方塊的長度和寬度
                  var w=30;var h=30;
                  
                  
          //開始動畫
                  interval = setInterval(function(){
                      
          //改變 y 坐標
                      y=y+delta;
                      
          //上邊緣檢測
                      if(y<0){
                          y
          =0;
                          delta
          =-delta;
                      }
                      
          //下邊緣檢測
                      if((y+h)>canvas.getAttribute("height")){
                          y
          =canvas.getAttribute("height")-h;
                          delta
          =-delta;
                      } 
                      
          //清空畫布
                      ctx.clearRect(0,0,canvas.getAttribute("width"),canvas.getAttribute("height"));
                      
          //保存狀態
                      ctx.save();
                      
          //移動坐標
                      ctx.translate(x,y);
                      
          //重新繪制
                      ctx.fillRect(0,0,w,h);
                      
          //恢復狀態
                      ctx.restore();
                  },
          1000/fps);
              }    
          </script>

           

           

          {{{{ 你的瀏覽器不支持 Canvas 標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          幀數:
          每次移動距離:
          }}}}

           

          三、重新組織代碼

          上面的代碼能正常工作了,但是存在很多問題,主要有以下幾點:

          1、計算方塊位置的代碼和繪制方塊的代碼混雜一起,即邏輯和視圖混雜,基本上不能擴展了

          2、代碼沒辦法復用,比如我們需要繪制多個不同的方塊對象:起始位置、大小、顏色、速度各不相同,每一種情況都需要重寫一遍。

          下面我們重新組織一下代碼,把方塊的共同屬性抽象出來,組成一個 Box 類,由這個 Box 類負責計算每一幀方塊的位置,這樣就可以解決上面兩個問題了。代碼如下:

          重新組織代碼
          <canvas id="canvas2" width="250" height="300" style="background-color:black">
              你的瀏覽器不支持 Canvas 標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          </canvas><br/>
          <input type="button" value="開始" onclick="move_box2()"/>
          <input type="button" value="暫停" onclick="stop()"/>

          <script type="text/javascript">
              
          //定時器
              var interval=null;
              
              
          //停止動畫
              function stop(){
                  clearInterval(interval);
              }

              
          //===================================================================
              
          //重新組織代碼
              
          //====================================================================
              
          //方塊的構造函數
              function Box(color,x,y,w,h,delta){
                  
          this.color=color;
                  
          this.x=x;
                  
          this.y=y;
                  
          this.w=w;
                  
          this.h=h;
                  
          this.delta=delta;
                  
          //三十幀
                  this.fps=30;
                  
          //每一幀的延遲時間
                  this.delay=1000/this.fps;
                  
          //上一次重繪的時間
                  this.last_update=0;
              }
              
              
          //方塊更新
              Box.prototype.update=function(canvas){
                  
          //獲取當前時間
                  var now=(new Date()).getTime();
                  
          //如果達到了延遲時間,則更新數據
                  if((now-this.last_update)>this.delay){
                  
                      
          //改變 y 坐標
                      this.y=this.y+this.delta;
                      
          //上邊緣檢測
                      if(this.y<0){
                          
          this.y=0;
                          
          this.delta=-this.delta;
                      }
                      
          //下邊緣檢測
                      if((this.y+this.h)>canvas.getAttribute("height")){
                          
          this.y=canvas.getAttribute("height")-this.h;
                          
          this.delta=-this.delta;
                      } 
                      
          //記下最新一次繪制時間
                      this.last_update=now;
                  }
                  
              }
              
              
              function move_box2(){
                  
          //停止動畫
                  stop();
                  
          //畫布對象
                  var canvas=document.getElementById("canvas2")
                  
          //獲取上下文對象
                  var ctx = canvas.getContext("2d");
                  
          //清空畫布
                  ctx.clearRect(0,0,canvas.getAttribute("width"),canvas.getAttribute("height"));
                  
                  
          //創建多個方塊對象
                  var boxes=[];
                  boxes[
          0]= new Box("red",3,2,10,35,2,10);//速度10
                  boxes[1]= new Box("blue",60,28,44,15,5);//速度20
                  boxes[2]= new Box("green",130,200,23,18,10);//速度30
                  boxes[3]= new Box("pink",200,150,35,10,20);//速度40
                  
                  
          //開始動畫繪制
                  interval = setInterval(function(){
                      
          for(var i=0;i<boxes.length;i++){
                          
          //取出一個方塊
                          var box=boxes[i];
                          
          //清空這個方塊
                          ctx.clearRect(box.x,box.y,box.w,box.h);
                          
          //更新數據
                          box.update(canvas);
                          
          //保存狀態
                          ctx.save();
                          
          //設置顏色
                          ctx.fillStyle=box.color;
                          
          //移動坐標
                          ctx.translate(box.x,box.y);
                          
          //重新繪制
                          ctx.fillRect(0,0,box.w,box.h);
                          
          //恢復狀態
                          ctx.restore();
                      }
                  },
          1);//盡可能快的循環
              }    
          </script>

           

          {{{{ 你的瀏覽器不支持 Canvas 標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          }}}}

           

          四、精靈登場

          據說在很久遠的年代,有多遠我也不知道,可能是任天堂紅白機是哪個年代吧,由于游戲機處理器的計算速度有限,所以專門設置了一個硬件用來處理角色圖像的相關數據,這些數據可能包括:

          1、計算當前的角色應該繪制哪一幀。上面我們的方塊雖然在移動,但是始終都是一個樣子;可是在游戲中,一個跑動的精靈,跑動動作是由很多幅連續的圖像組成,我們需要知道現在應該繪制其中的哪一幅圖像;

          2、表現精靈動作的很多幅連續的圖像通常是集中放置在一個大圖中,我們需要計算當前繪制的那一幅,在大圖中處于什么位置,并把它截取出來

          上面說到這個硬件,曾經被叫做 Sprite 精靈。現如今,我們的處理器已經十分強大,不再需要 Sprite 這樣的輔助硬件,但是這樣的功能仍然需要,只不過用軟件來實現罷了,所以,我們依然用 Sprite 來稱呼游戲中的一個角色。

          這里有一幅圖像,他描繪了一個小精靈的飛行動作

           

          下面我們將實現一個 Sprite 類,讓他在瀏覽器里面飛起來。

          精靈登場
          <canvas id="canvas3" width="250" height="300" style="background-color:black">
              你的瀏覽器不支持 
          &lt;canvas&gt;標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          </canvas><br/>
          幀數:
          <input  id="txt4" type="text" value="10"/><br/>
          速度:
          <input type="text" id="txt5" value="5"/><br/>
          比例:
          <input type="text" id="txt6" value="2"/><br/>
          <input type="button" value="開始" onclick="animate()"/>
          <input type="button" value="暫停" onclick="stop()"/>

          <script type="text/javascript">
              
          //定時器
              var interval=null;
              
              
          //停止動畫
              function stop(){
                  clearInterval(interval);
              }
              
              
          //===================================================================
              
          //精靈登場
              
          //====================================================================
              
          //每一幀在大圖中的位置
              var frames=[];
              frames[
          0]=[0,4,19,19];
              frames[
          1]=[22,1,24,19];
              frames[
          2]=[49,0,18,17];
              frames[
          3]=[1,32,18,17];
              frames[
          4]=[22,33,24,19];
              frames[
          5]=[49,36,19,19];
              
              
          //精靈類
              function Sprite(dx,dy,delta,fps){
                  
          this.dx=dx;
                  
          this.dy=dy;
                  
          this.fps=fps;
                  
          this.delay=1000/fps;
                  
          this.last_update=0;
                  
          //移動速度
                  this.delta=-delta;
                  
          //幀編號
                  this.index=0;
                  
          //方向
                  this.dir_left=true;
              }
              
              Sprite.prototype.update
          =function(canvas){
                  
          //獲取當前時間
                  var now=(new Date()).getTime();
                  
          if((now-this.last_update)>this.delay){
                      
          if(this.dir_left){
                          
          //方向朝左,只繪制0 1 2幀
                          if(this.index>2)
                              
          this.index=0;
                      }
                      
          else{
                          
          //方向朝右,只繪制 3 4 5 幀
                          if(this.index>5)
                              
          this.index=3;
                      }
                      
          //取出當前幀的坐標
                      this.frame=frames[this.index];
                      
                      
          //當前幀在大圖中的位置
                      this.sx=this.frame[0];
                      
          this.sy=this.frame[1];
                      
          this.sw=this.frame[2];
                      
          this.sh=this.frame[3];
                      
                      
          //當前幀大小
                      this.dw=this.frame[2];
                      
          this.dh=this.frame[3];
                      
                      
          //改變 x 坐標
                      this.dx=this.dx+this.delta;
                      
          //左邊緣檢測
                      if(this.dx<0){
                          
          this.dx=0;
                          
          //轉向
                          this.delta=-this.delta;
                          
          this.dir_left=false;
                          
          this.index=3;
                      }
                      
          //右邊緣檢測
                      if((this.dx+this.dw)>canvas.getAttribute("width")){
                          
          this.dx=canvas.getAttribute("width")-this.dw;
                          
          //轉向
                          this.delta=-this.delta;
                          
          this.dir_left=true;
                          
          this.index=0;
                      }         
                      
          this.dy=this.dy;//y 不移動
                      

                      
          this.index++;
                      
          this.last_update=now;
                  }
              }
              
              function animate(){
                  
          //停止動畫
                  stop();
                  
          //移動速度
                  var delta=parseInt(document.getElementById('txt4').value);
                  
          //每秒繪制多少次
                  var fps=parseInt(document.getElementById('txt5').value);
                  
          //比例
                  var scale=parseInt(document.getElementById('txt6').value);
                  
                  
          //畫布對象
                  var canvas=document.getElementById("canvas3")
                  
          //獲取上下文對象
                  var ctx = canvas.getContext("2d");
                  
          //清空畫布
                  ctx.clearRect(0,0,canvas.getAttribute("width"),canvas.getAttribute("height"));
                  
                  var img
          =new Image();
                  img.src
          ="http://images.cnblogs.com/cnblogs_com/myqiao/html5/sprite.gif";

                  var sprite
          =new Sprite(120,150,delta,fps);
                  interval 
          = setInterval(function(){
                      
          //清空畫布
                      ctx.clearRect(0,0,canvas.getAttribute("width"),canvas.getAttribute("height"));
                      
          //更新數據
                      sprite.update(canvas);
                      
          //保存狀態
                      ctx.save();
                      
          //移動坐標
                      ctx.translate(sprite.dx,sprite.dy);
                      ctx.scale(scale,scale);
                      ctx.drawImage(img,sprite.sx,sprite.sy,sprite.sw,sprite.sh,
          0,0,sprite.dw,sprite.dh);
                      
          //恢復狀態
                      ctx.restore();
                  },
          1);
                  
              }
              
          </script>

          {{{ 你的瀏覽器不支持 <canvas>標簽,請使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          幀數:
          速度:
          比例:
          }}}

           

           

           

           


          //==========================================

          posted on 2010-10-08 00:14 左洸 閱讀(2526) 評論(1)  編輯  收藏 所屬分類: HTML5

          FeedBack:
          # re: HTML5邊玩邊學(7):動畫初步 之 飛舞的精靈
          2010-10-08 00:23 | 別玩HTML5了,閑的蛋疼
          呵呵,沒仔細看樓主的文章內容,看了標題HTML5就煩了,如果樓主改個標題似乎更好,一個2d編程應用系列之類的。我最近也在弄qt,可以仔細的看看樓主的文章,看看能否舉一反三,呵呵  回復  更多評論
            
          主站蜘蛛池模板: 高密市| 仁怀市| 偏关县| 壶关县| 翁牛特旗| 武宣县| 台山市| 油尖旺区| 泰和县| 清新县| 治多县| 尉犁县| 高碑店市| 衡阳市| 甘德县| 尚义县| 定州市| 会昌县| 宿松县| 大宁县| 平塘县| 伊川县| 高要市| 乌兰浩特市| 西平县| 永修县| 内丘县| 铅山县| 静宁县| 黑水县| 襄垣县| 全南县| 永胜县| 孝义市| 福州市| 莆田市| 夏津县| 通海县| 无锡市| 鄯善县| 界首市|