一、為什麼選擇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 相關的資料。
下面我們給出一個上下移動方塊的小動畫,當遇到頂部或底部時,會改變方向。程式碼如下:
基本动画 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><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>
三、重新組織程式碼
上面的程式碼能正常運作了,但是存在很多問題,主要有以下幾點:
1、計算方塊位置的程式碼和繪製方塊的程式碼混雜一起,即邏輯和視圖混雜,基本上不能擴展了
2、代码没办法复用,比如我们需要绘制多个不同的方块对象:起始位置、大小、颜色、速度各不相同,每一种情况都需要重写一遍。
下面我们重新组织一下代码,把方块的共同属性抽象出来,组成一个 Box 类,由这个 Box 类负责计算每一帧方块的位置,这样就可以解决上面两个问题了。代码如下:
重新组织代码 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><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>
四、精灵登场
据说在很久远的年代,有多远我也不知道,可能是任天堂红白机是哪个年代吧,由于游戏机处理器的计算速度有限,所以专门设置了一个硬件用来处理角色图像的相关数据,这些数据可能包括:
1、计算当前的角色应该绘制哪一帧。上面我们的方块虽然在移动,但是始终都是一个样子;可是在游戏中,一个跑动的精灵,跑动动作是由很多幅连续的图像组成,我们需要知道现在应该绘制其中的哪一幅图像;
2、表现精灵动作的很多幅连续的图像通常是集中放置在一个大图中,我们需要计算当前绘制的那一幅,在大图中处于什么位置,并把它截取出来
上面说到这个硬件,曾经被叫做 Sprite 精灵。现如今,我们的处理器已经十分强大,不再需要 Sprite 这样的辅助硬件,但是这样的功能仍然需要,只不过用软件来实现罢了,所以,我们依然用 Sprite 来称呼游戏中的一个角色。
这里有一幅图像,他描绘了一个小精灵的飞行动作
下面我们将实现一个 Sprite 类,让他在浏览器里面飞起来。
精灵登场 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><canvas id="canvas3" width="250" height="300" style="background-color:black"> 你的浏览器不支持 <canvas>标签,请使用 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>
以上是HTML5邊玩邊學(七)-動畫初步之飛舞的精靈的詳細內容。更多資訊請關注PHP中文網其他相關文章!