canvas入門(mén)基礎(chǔ)(九):基礎(chǔ)動(dòng)畫(huà)

2018-06-19 14:41 更新
實(shí)現(xiàn)動(dòng)畫(huà),我們首先想到的肯定是setTimeout和setInterval,這兩個(gè)在這里就不細(xì)說(shuō)了。

除了這兩個(gè)外,我們還可以使用window.requestAnimationFrame()這個(gè)方法。

requestAnimationFrame 是專門(mén)為實(shí)現(xiàn)高性能的幀動(dòng)畫(huà)而設(shè)計(jì)的一個(gè)API

window.requestAnimationFrame()這個(gè)方法是用來(lái)在頁(yè)面重繪之前,通知瀏覽器調(diào)用一個(gè)指定的函數(shù),以滿足開(kāi)發(fā)者操作動(dòng)畫(huà)的需求。這個(gè)方法接受一個(gè)函數(shù)為參,該函數(shù)會(huì)在重繪前調(diào)用。
注意: 如果想得到連貫的逐幀動(dòng)畫(huà),函數(shù)中必須重新調(diào)用 requestAnimationFrame()。
如果你想做逐幀動(dòng)畫(huà)的時(shí)候,你應(yīng)該用這個(gè)方法。這就要求你的動(dòng)畫(huà)函數(shù)執(zhí)行會(huì)先于瀏覽器重繪動(dòng)作。通常來(lái)說(shuō),被調(diào)用的頻率是每秒60次,但是一般會(huì)遵循W3C標(biāo)準(zhǔn)規(guī)定的頻率。如果是后臺(tái)標(biāo)簽頁(yè)面,重繪頻率則會(huì)大大降低。

回調(diào)函數(shù)只會(huì)被傳入一個(gè)DOMHighResTimeStamp參數(shù),這個(gè)參數(shù)指示當(dāng)前被 requestAnimationFrame 序列化的函數(shù)隊(duì)列被觸發(fā)的時(shí)間。因?yàn)楹芏鄠€(gè)函數(shù)在這一幀被執(zhí)行,所以每個(gè)函數(shù)都將被傳入一個(gè)相同的時(shí)間戳,盡管經(jīng)過(guò)了之前很多的計(jì)算工作。這個(gè)數(shù)值是一個(gè)小數(shù),單位毫秒,精確度在 10 μs。

參數(shù) callback 在每次需要重新繪制動(dòng)畫(huà)時(shí),會(huì)調(diào)用這個(gè)參數(shù)所指定的函數(shù)。這個(gè)回調(diào)函數(shù)會(huì)收到一個(gè)參數(shù),這個(gè) DOMHighResTimeStamp 類型的參數(shù)指示當(dāng)前時(shí)間距離開(kāi)始觸發(fā) requestAnimationFrame 的回調(diào)的時(shí)間。 返回值 requestID 是一個(gè)長(zhǎng)整型非零值,作為一個(gè)唯一的標(biāo)識(shí)符.你可以將該值作為參數(shù)傳給 window.cancelAnimationFrame() 來(lái)取消這個(gè)回調(diào)函數(shù)。

//動(dòng)畫(huà)循環(huán) if (!window.requestAnimationFrame) { window.requestAnimationFrame = (window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function (callback) { return window.setTimeout(callback, 17 ); }); }


 if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = (window.cancelRequestAnimationFrame || window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelAnimationFrame || window.mozCancelRequestAnimationFrame || window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame || window.oCancelAnimationFrame || window.oCancelRequestAnimationFrame || window.clearTimeout); }

例子:

var cc = document.getElementById("canvas");   

var cxt = cc.getContext("2d");   

var ca = document.createElement("canvas");   

ca.width=500;   

ca.height=500;   

var cxt2 = ca.getContext("2d");   

var x = y = 0;  

//加載多個(gè)圖片

function loadImages(sources, callback) {  

    var count = 0,    

    images = {},   

    imgNum = 0;   

    for (k in sources) {   

        imgNum++;   

    }    

    for (k in sources) {   

        images[k] = new Image();   

        images[k].onload = function() {   

            if (++count >= imgNum) {   

                callback(images);   

            }   

        }   

        images[k].src = sources[k];   

    }   

}   

var sources = []; 


function drawGameImage() {    

    sources = ["gameimage/0.png", "gameimage/1.png", "gameimage/2.png", "gameimage/3.png", "gameimage/4.png", "gameimage/5.png", "gameimage/6.png", "gameimage/7.png", "gameimage/8.png", "gameimage/2.png", "gameimage/3.png", "gameimage/4.png", "gameimage/5.png", "gameimage/6.png", "gameimage/9.png", "gameimage/10.png"];     //調(diào)用圖片預(yù)加載函數(shù),實(shí)現(xiàn)回調(diào)函數(shù)     

    loadImages(sources, function(images) {   

        var x = y = 0;   

        for (var i = 0; i < sources.length; i++) {   

            cxt2.drawImage(images[i], x * 100, y * 100, 100, 100);   

            if (i < 4) {

                 x++; 

             } else if (i >= 4 && i < 8) {   

                  y++;   

             } else if (i >= 8 && i < 12) {   

                  x--;   

             } else {   

                   y--;

             } 

        }    

     });   

}    

drawGameImage();   

var times = Math.floor(Math.random() * 10) + sources.length * 2;   

var v = 0;    

function move() {   

     var moveX = 400;

     var speed = 100; 

     if (x < moveX && y == 0) {   

        x += speed;

     } else if (x == moveX && y < moveX) { 

        y += speed;   

     } else if (y == moveX && x > 0) {   

         x -= speed;   

     } else if (x == 0 && y > 0) {   

         y -= speed;   

     }   

     cxt.clearRect(0, 0, 500, 500);   

     cxt.drawImage(ca,0,0,500,500);   

     cxt.fillStyle = "rgba(0,0,0,.5)";   

     cxt.fillRect(x, y, 100, 100);   

     if (v > times) {   

         cancelAnimationFrame();   

     }   

     v++;   

      requestAnimationFrame(move);   

}   

move();



我們來(lái)逐一看看:

首先是加載多個(gè)圖片問(wèn)題,我在這里封裝了一個(gè)loadImages(sources,callback)方法,其中sources是一個(gè)圖片路徑數(shù)組,callback是圖片加載完成后的回調(diào)函數(shù)。

function loadImages(sources, callback) { var count = 0, images = {}, imgNum = 0; for (k in sources) { imgNum++; } for (k in sources) { images[k] = new Image(); images[k].onload = function() { if (++count >= imgNum) { callback(images); } } images[k].src = sources[k]; } }   


注意:
  1. 在loadImages方法中,我們應(yīng)該先綁定image.onload事件,后加載圖片
原因:如果圖片從緩存中加載,速度非常快以至于沒(méi)來(lái)得及綁定事件就加載完畢,自然不會(huì)觸發(fā)綁定事件。

  1. for...in循環(huán) 與 for循環(huán)的區(qū)別
for循環(huán)用于迭代數(shù)組(array) for...in循環(huán)用于迭代對(duì)象(object, {})或者關(guān)聯(lián)數(shù)組(hash array)

由于我們的背景是不需要變化,而滾動(dòng)框是需要不停的重繪渲染,所以我們這里創(chuàng)建了一個(gè)新的canvas,將背景圖繪制在這個(gè)canvas上,然后將這個(gè)canvas作為圖片源,繪制在第一個(gè)canvas里。

var ca = document.createElement("canvas"); ca.width=500; ca.height=500; var cxt2 = ca.getContext("2d");   


 drawGameImage();   

當(dāng)然,你也可以用分層canvas來(lái)分開(kāi)不需頻繁渲染的圖形和需要頻繁重繪渲染的圖形。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)