HTML5 canvas 元素詳細教程七:基本的動畫。
由于我們是用腳本去操控 canvas 對象,這樣要實現一些交互動畫也是相當容易的。只不過,canvas 從來都不是專門為動畫而設計的(不像 Flash),難免會有些限制。
可能最大的限制就是圖像一旦繪制出來,它就是一直保持那樣了。如果需要移動它,我們不得不對所有東西(包括之前的)進行重繪。重繪是相當費時的,而且性能很依賴于電腦的速度。
基本動畫的步驟 Basic animation steps
畫一幀,你需要以下一些步驟:
- 清空 canvas
除非接下來要畫的內容會完全充滿 canvas (例如背景圖),否則你需要清空所有。最簡單的做法就是用clearRect
方法。 - 保存 canvas 狀態
如果你要改變一些會改變 canvas 狀態的設置(樣式,變形之類的),又要在每畫一幀之時都是原始狀態的話,你需要先保存一下。 - 繪制動畫圖形(animated shapes)
這一步才是重繪動畫幀。 - 恢復 canvas 狀態
如果已經保存了 canvas 的狀態,可以先恢復它,然后重繪下一幀。
操控動畫 Controlling an animation
在 canvas 上繪制內容是用 canvas 提供的或者自定義的方法,而通常,我們僅僅在腳本執行結束后才能看見結果,比如說,在 for 循環里面做完成動畫是不太可能的。
我們需要一些可以定時的執行重繪的方法。有兩種方法可以實現這樣的動畫操控。首先可以通過 setInterval
和 setTimeout
方法來控制在設定的時間點上執行重繪。
setInterval(animateShape,500); setTimeout(animateShape,500); 如果你不需要任何交互操作,用setInterval
方法定時執行重繪是最適合的了。在上面的例子(leegorous不見了?)里animateShape
方法每半秒執行一次。setTimeout
方法只會在預設時間點上執行操作。 第二個方法,我們可以利用用戶輸入來實現操控。如果需要做一個游戲,我們可以通過監聽用戶交互過程中觸發的事件(如 keyboard,mouse)來控制動畫效果。
下面的例子,我還使用第一種方式實現動畫效果。頁面底部有些鏈接,那些是應用第二種方法的例子。
動畫例子 1
這個例子里面,我會讓一個小型的太陽系模擬系統動起來。
var sun = new Image(); var moon = new Image(); var earth = new Image(); function init(){ sun.src = 'images/sun.png'; moon.src = 'images/moon.png'; earth.src = 'images/earth.png'; setInterval(draw,100); } function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.globalCompositeOperation = 'destination-over'; ctx.clearRect(0,0,300,300); // clear canvas ctx.fillStyle = 'rgba(0,0,0,0.4)'; ctx.strokeStyle = 'rgba(0,153,255,0.4)'; ctx.save(); ctx.translate(150,150); // Earth var time = new Date(); ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() ); ctx.translate(105,0); ctx.fillRect(0,-12,50,24); // Shadow ctx.drawImage(earth,-12,-12); // Moon ctx.save(); ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() ); ctx.translate(0,28.5); ctx.drawImage(moon,-3.5,-3.5); ctx.restore(); ctx.restore(); ctx.beginPath(); ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit ctx.stroke(); ctx.drawImage(sun,0,0,300,300); }
動畫例子 2
function init(){ clock(); setInterval(clock,1000); } function clock(){ var now = new Date(); var ctx = document.getElementById('canvas').getContext('2d'); ctx.save(); ctx.clearRect(0,0,150,150); ctx.translate(75,75); ctx.scale(0.4,0.4); ctx.rotate(-Math.PI/2); ctx.strokeStyle = "black"; ctx.fillStyle = "white"; ctx.lineWidth = 8; ctx.lineCap = "round"; // Hour marks ctx.save(); for (var i=0;i<12;i++){ ctx.beginPath(); ctx.rotate(Math.PI/6); ctx.moveTo(100,0); ctx.lineTo(120,0); ctx.stroke(); } ctx.restore(); // Minute marks ctx.save(); ctx.lineWidth = 5; for (i=0;i<60;i++){ if (i%5!=0) { ctx.beginPath(); ctx.moveTo(117,0); ctx.lineTo(120,0); ctx.stroke(); } ctx.rotate(Math.PI/30); } ctx.restore(); var sec = now.getSeconds(); var min = now.getMinutes(); var hr = now.getHours(); hr = hr>=12 ? hr-12 : hr; ctx.fillStyle = "black"; // write Hours ctx.save(); ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec ) ctx.lineWidth = 14; ctx.beginPath(); ctx.moveTo(-20,0); ctx.lineTo(80,0); ctx.stroke(); ctx.restore(); // write Minutes ctx.save(); ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec ) ctx.lineWidth = 10; ctx.beginPath(); ctx.moveTo(-28,0); ctx.lineTo(112,0); ctx.stroke(); ctx.restore(); // Write seconds ctx.save(); ctx.rotate(sec * Math.PI/30); ctx.strokeStyle = "#D40000"; ctx.fillStyle = "#D40000"; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(-30,0); ctx.lineTo(83,0); ctx.stroke(); ctx.beginPath(); ctx.arc(0,0,10,0,Math.PI*2,true); ctx.fill(); ctx.beginPath(); ctx.arc(95,0,10,0,Math.PI*2,true); ctx.stroke(); ctx.fillStyle = "#555"; ctx.arc(0,0,3,0,Math.PI*2,true); ctx.fill(); ctx.restore(); ctx.beginPath(); ctx.lineWidth = 14; ctx.strokeStyle = '#325FA2'; ctx.arc(0,0,142,0,Math.PI*2,true); ctx.stroke(); ctx.restore(); }
動畫例子 3
這是一個從左到右滾動的全景動畫的代碼。請注意圖片的大小需要比 canvas 大。
本例中用的是這張圖片:
http://www.css3-html5.com/uploadfile/2011/0721/20110721020253661.jpg
var img = new Image(); //User Variables img.src = 'Capitan_Meadows,_Yosemite_National_Park.jpg'; var CanvasXSize = 800; var CanvasYSize = 200; var speed = 30; //lower is faster var scale = 1.05; var y = -4.5; //vertical offset //End User Variables var dx = 0.75; var imgW = img.width*scale; var imgH = img.height*scale; var x = 0; if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas var clearX; var clearY; if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas else { clearX = CanvasXSize; } if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas else { clearY = CanvasYSize; } var ctx; function init() { //Get Canvas Element ctx = document.getElementById('canvas').getContext('2d'); //Set Refresh Rate return setInterval(draw, speed); } function draw() { //Clear Canvas ctx.clearRect(0,0,clearX,clearY); //If image is <= Canvas Size if (imgW <= CanvasXSize) { //reset, start from beginning if (x > (CanvasXSize)) { x = 0; } //draw aditional image if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); } } //If image is > Canvas Size else { //reset, start from beginning if (x > (CanvasXSize)) { x = CanvasXSize-imgW; } //draw aditional image if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); } } //draw image ctx.drawImage(img,x,y,imgW,imgH); //amount to move x += dx; }
html code. Canvas width and height should match the CanvasXSize, CanvasYSize.
<body onload="init();"> <canvas id="canvas" width="800" height="200"></canvas>
其它例子
- Canvascape:一個 3D (第一人稱射擊)冒險游戲。
- Flying through a starfield:在星星,圓圈,方塊間滑翔。
- iGrapher:股票數據圖表的例子。
HTML5 Canvas 的詳細教程就先在這結束,以后有更好的教程,我們會第一時間分享給大家。