首頁 >web前端 >H5教程 >HTML5邊玩邊學(七)-動畫初步之飛舞的精靈

HTML5邊玩邊學(七)-動畫初步之飛舞的精靈

黄舟
黄舟原創
2017-03-29 15:10:141231瀏覽

一、為什麼選擇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(&#39;txt1&#39;).value);

        //每秒绘制多少次

        var fps=parseInt(document.getElementById(&#39;txt2&#39;).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">

    你的浏览器不支持 &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(&#39;txt4&#39;).value);

        //每秒绘制多少次

        var fps=parseInt(document.getElementById(&#39;txt5&#39;).value);

        //比例

        var scale=parseInt(document.getElementById(&#39;txt6&#39;).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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn