【實例】html5-canvas中實現背景圖片的移動

本例來自於《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>