本例來自於《HTML5 Canvas核心技術 圖形、動畫與遊戲開發》javascript
在線演示 (圖有點多,請多刷新幾回)css
本例還有一點代碼不理解,我用註釋和問號標註出來了,有大神看到求解答,謝謝html
本例子難點主要在經過當前的FPS計算圖像下一幀的顯示座標,這也是我不理解的地方java
還有就是requestAnimationFrame這個,這個是用來以瀏覽器最合適的方式循環執行一些代碼canvas
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>圖片運動實例</title> <style type="text/css"> body { background: #DDDDDD; } #canvas { border: thin solid black; } input { margin-left: 15px; } </style> </head> <body> <canvas id="canvas" width="1000" height="440"> 您的瀏覽器不支持canvas,請更新到最新的瀏覽器 </canvas> <input type="button" name="animateButton" id="animateButton" value="運動" /> <hr /> <div> <table border="1" cellspacing="" cellpadding=""> <tr> <td>FPS</td> <td><span id="FPS"></span></td> </tr> <tr> <td>SKY_VELOCITY/fps</td> <td><span id="SKY_VELOCITY"></span></td> </tr> <tr> <td>GRASS_VELOCITY/fps</td> <td><span id="GRASS_VELOCITY"></span></td> </tr> <tr> <td>TREE_VELOCITY/fps</td> <td><span id="TREE_VELOCITY"></span></td> </tr> <tr> <td>FAST_TREE_VELOCITY/fps</td> <td><span id="FAST_TREE_VELOCITY"></span></td> </tr> </table> </div> </body> <script type="text/javascript"> var canvas = document.getElementById("canvas"), context = canvas.getContext("2d"), animateButton = document.getElementById("animateButton"), //建立多個圖像對象 sky = new Image(), tree = new Image(), nearTree = new Image(), grass = new Image(), grass2 = new Image(), redRect = new Image, paused = true, lastTime = 0, fps = 0, //當前的幀速率 //都是不一樣圖像 skyOffset = 0, grassOffset = 0, treeOffset = 0, nearTreeOffset = 0, TREE_VELOCITY = 20, FAST_TREE_VELOCITY = 40, SKY_VELOCITY = 8, GRASS_VELOCITY = 75; //如下是檢測對象 var SKY_VELOCITYFps = document.getElementById("SKY_VELOCITY"); var GRASS_VELOCITYFps = document.getElementById("GRASS_VELOCITY"); var TREE_VELOCITYFps = document.getElementById("TREE_VELOCITY"); var FAST_TREE_VELOCITYFps = document.getElementById("FAST_TREE_VELOCITY"); var FPS = document.getElementById("FPS"); //擦除方法 function erase() { context.clearRect(0, 0, canvas.width, canvas.height); } function draw() { context.save(); //fps是共用的 //應該是肯定當前圖像位置的座標X //???如下四行仍是有點不太理解,只知道這個能夠用來循環的顯示image對象,至於SKY_VELOCITY/fps是什麼還不清楚 skyOffset = skyOffset < canvas.width ? skyOffset + SKY_VELOCITY/fps : 0; grassOffset = grassOffset < canvas.width ? grassOffset + GRASS_VELOCITY/fps : 0; treeOffset = treeOffset < canvas.width ? treeOffset + TREE_VELOCITY/fps : 0; nearTreeOffset = nearTree < canvas.width ? nearTree + FAST_TREE_VELOCITY/fps : 0; //檢測數據 SKY_VELOCITYFps.innerHTML = SKY_VELOCITY/fps; GRASS_VELOCITYFps.innerHTML = GRASS_VELOCITY/fps; TREE_VELOCITYFps.innerHTML = TREE_VELOCITY/fps; FAST_TREE_VELOCITYFps.innerHTML = FAST_TREE_VELOCITY/fps; FPS.innerHTML = fps; //像下面幾行這樣寫的後果:canvas運動到後面的時候就可能沒有圖像對象了 // skyOffset += SKY_VELOCITY/fps; // grassOffset += GRASS_VELOCITY/fps; // treeOffset += TREE_VELOCITY/fps; // nearTreeOffset += FAST_TREE_VELOCITY/fps; //開始繪製天空 context.save(); //改變座標原點 context.translate(-skyOffset, 0); //繪製一個天空 context.drawImage(sky, 0, 0); //在即將天空結尾的時候在繪製一個天空用來銜接 context.drawImage(sky, sky.width-2, 0); //將改變的座標原點改回來 context.restore(); //開始繪製遠處的樹 //此處繪製的位置有的地方出如今canvas.width以外 context.save(); //改變座標原點 context.translate(-treeOffset, 0); //以改變原點後的座標上繪製好多個樹 context.drawImage(tree, 100, 240); context.drawImage(tree, 1100, 240); context.drawImage(tree, 400, 240); context.drawImage(tree, 1400, 240); context.drawImage(tree, 700, 240); context.drawImage(tree, 1700, 240); //恢復座標原點 context.restore(); //繪製近處的樹木,他的運行速度更加的塊 context.save(); context.translate(-nearTreeOffset, 0); context.drawImage(nearTree, 250, 220); context.drawImage(nearTree, 1250, 220); context.drawImage(nearTree, 800, 220); context.drawImage(nearTree, 1800, 220); context.restore(); //繪製綠地 context.save(); context.translate(-grassOffset, 0); //在canvas底部繪製草地image對象 context.drawImage(grass, 0, canvas.height-grass.height); context.drawImage(grass, grass.width, canvas.height-grass.height); context.drawImage(grass2, 0, canvas.height-grass2.height); context.drawImage(grass2, grass2.width, canvas.height-grass2.height); context.restore(); } //計算當前的FPS function calculateFps(now) { //1000毫秒除以距離上一次計算FPS的時間意思是,或者距離上一次繪製圖像 //這段時間裏面執行了多少次 //除以1000是由於now和lastTime都是以毫秒爲單位的 //反正fps就是一秒內可以執行繪製多少次嘛~因此的 var fps = 1000 / (now - lastTime); lastTime = now; return fps; } function animate(now) { //若是當前時間尚未定義 if(now === undefined) { //就把當前時間賦值給now now = +new Date; } fps = calculateFps(now); //只要不是暫停狀態就執行 if(!paused) { //擦除canvas //原例子中是用兼容的手法實現requestAnimationFrame,因此須要erase // erase(); //立刻繪製新的 draw(); } //以最適合的速度執行animate中的內容,這個比較適合動畫的處理,相似於setTimeout requestAnimationFrame(animate); } animateButton.onclick = function(e) { paused = paused ? false : true; if(paused) { animateButton.value = "運動"; } else { animateButton.value = "暫停"; } }; // canvas.width = canvas.width; // canvas.height = canvas.height; sky.src = "sky.png"; tree.src = "smalltree.png"; nearTree.src = "tree-twotrunks.png"; grass.src = "grass.png"; grass2.src = "grass2.png"; sky.onload = function(e) { draw(); }; // requestAnimationFrame(animate); //啓動 animate(); </script> </html>