本例来自于《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>