首页 > 解决方案 > 尝试创建 HTML Canvas Tiles 并通过对象移动使其动态化

问题描述

尝试用图块创建一个画布,如图所示,尝试向各个方向移动车辆对象,是否有实现此功能的解决方案?

功能:每当车辆对象在网格上移动时,网格应根据车辆移动而旋转,类似于谷歌地图的功能。

谁能给我解决方案。

显示在首页的图像

标签: javascriptcssangularcanvashtml5-canvas

解决方案


如果您使用偏移量来绘制所有对象并执行一些屏幕外剔除,则“无限”画布很容易。

通过单击画布在视图之间切换。

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				display: block;
				margin: auto;
				border: solid 1px white;
				border-radius: 10px;
			}
		</style>
	</head>
	
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
		
		void function() {
			
			"use strict";
			
			// lerp + 0.0 -> 1.0 smooth step
			function interpolate(value_1,value_2,t) {
				t = Math.max(0.0,Math.min(t,1.0));
				t = t * t * (3.0 - 2.0 * t);
				return value_1 + t * (value_2 - value_1);
			}
			
			function Camera(x,y) {
				this.x = x;
				this.y = y;
				this.angle = 0.0;
				this.zoom = 1.0;
				this.startX = 0.0;
				this.startY = 0.0;
				this.startAngle = 0.0;
				this.startZoom = 0.0;
				this.destX = 0.0;
				this.destY = 0.0;
				this.destAngle = 0.0;
				this.destZoom = 0.0;
				this.moveT = 0.0;
				this.target = null;
				this.targetOffsetX = 0.0;
				this.targetOffsetY = 0.0;
				this.state = this.STATE_IDLE;
			}
			
			Camera.prototype = {
				// States
				STATE_IDLE: 0,
				STATE_MOVING: 1,
				STATE_TRACKING: 2,
			
				// Const vars
				MOVE_T_INC: 0.01,
			
				set: function(x,y,angle,zoom) {
					this.destX = x;
					this.destY = y;
					this.destAngle = angle;
					this.destZoom = zoom;
					this.startX = this.x;
					this.startY = this.y;
					this.startAngle = this.angle;
					this.startZoom = this.zoom;
					this.moveT = 0.0;
					this.target = null;
					this.state = this.STATE_MOVING;
				},
				
				track: function(object,offsetX,offsetY,zoom) {
					if (object) {
						this.target = object;
						this.targetOffsetX = offsetX || 0.0;
						this.targetOffsetY = offsetY || 0.0;
						this.destX = object.x + this.targetOffsetX;
						this.destY = object.y + this.targetOffsetY;
						this.destAngle = object.angle;
						this.destZoom = zoom || this.zoom;
						this.startX = this.x;
						this.startY = this.y;
						this.startAngle = this.angle;
						this.startZoom = this.zoom;
						this.moveT = 0.0;
						this.state = this.STATE_MOVING;
					}
				},
				
				tick: function() {
					switch(this.state) {
						case this.STATE_MOVING: {
							if (this.target) {
								this.destX = this.target.x + this.targetOffsetX;
								this.destY = this.target.y + this.targetOffsetY;
								this.destAngle = this.target.angle;
							}
							
							this.x = interpolate(this.startX,this.destX,this.moveT);
							this.y = interpolate(this.startY,this.destY,this.moveT);
							this.angle = interpolate(this.startAngle,this.destAngle,this.moveT);
							this.zoom = interpolate(this.startZoom,this.destZoom,this.moveT);
							this.moveT += this.MOVE_T_INC;
							
							if (this.moveT >= 1.0) {
								this.state = this.target ? this.STATE_TRACKING : this.STATE_IDLE;
							}
						} break;
						
						case this.STATE_TRACKING: {
							if (this.target) {
								this.x = this.target.x + this.targetOffsetX;
								this.y = this.target.y + this.targetOffsetY;
								this.angle = this.target.angle;
							} else {
								this.state = this.STATE_IDLE;
							}
						}
					}
				}
			};
			
			function Point(x,y) {
				this.x = x;
				this.y = y;
			}
			
			function Vehicle(x,y,path) {
				this.x = x;
				this.y = y;
				this.path = path;
				this.currentPoint = 0;
				this.angle = 0.0;
				this.t = 0.0;
				this.startAngle = 0.0;
				this.targetAngle = 0.0;
				
				var point = this.path[this.currentPoint];
				var x = point.x - this.x;
				var y = point.y - this.y;
				var distance = 1.0 / Math.sqrt(Math.pow(x,2.0) + Math.pow(y,2.0));
	
				this.t = 0.0;
				this.startAngle = this.angle;
				this.targetAngle = Math.atan2(y * distance,x * distance) + Math.PI * 0.5;
				this.state = this.STATE_TURNING;
			}
			
			Vehicle.prototype = {
				WIDTH: 20.0,
				HEIGHT: 40.0,
				TURN_SPEED: 0.01,
				MOVE_SPEED: 1.0,
				POINT_RADIUS: 1.0,
				
				STATE_MOVING: 0,
				STATE_TURNING: 1,
			
				tick: function() {
					var point = this.path[this.currentPoint];
					var x = point.x - this.x;
					var y = point.y - this.y;
					var distance = Math.sqrt(Math.pow(x,2.0) + Math.pow(y,2.0));
					
					switch(this.state) {
						case this.STATE_MOVING: {
							if (distance < this.POINT_RADIUS) {
								if (++this.currentPoint === this.path.length) {
									this.currentPoint = 0;
								}
								
								point = this.path[this.currentPoint];
								x = point.x - this.x;
								y = point.y - this.y;
								distance = 1.0 / Math.sqrt(Math.pow(x,2.0) + Math.pow(y,2.0));
					
								this.t = 0.0;
								this.startAngle = this.angle;
								this.targetAngle = Math.atan2(y * distance,x * distance) + Math.PI * 0.5;
								this.state = this.STATE_TURNING;
							} else {
								distance = 1.0 / distance;
								this.x += x * distance * this.MOVE_SPEED;
								this.y += y * distance * this.MOVE_SPEED;
							}
						} break;
						
						case this.STATE_TURNING: {
							this.angle = interpolate(this.startAngle,this.targetAngle,this.t);
							this.t += this.TURN_SPEED;
							
							if (this.t >= 1.0) {
								this.state = this.STATE_MOVING;
							}
						} break;
					}
				},
				
				render: function(ctx,camera) {
					var x = (this.x - camera.x) * camera.zoom;
					var y = (this.y - camera.y) * camera.zoom;
					var angle = this.angle;
					var width = this.WIDTH * camera.zoom;
					var height = this.HEIGHT * camera.zoom;
				
					ctx.strokeStyle = "black";
					ctx.fillStyle = "darkred";
					ctx.translate(x,y);
					ctx.rotate(angle);
					ctx.beginPath();
					
					ctx.rect(
						-width * 0.5,
						-height * 0.5,
						width,
						height
					);
					
					ctx.fill();
					ctx.stroke();
					ctx.rotate(-angle);
					ctx.translate(-x,-y);
				}
			};
			
			var tileWidth = 32;
			var tileHeight = 32;
			var invTileWidth = 1.0 / tileWidth;
			var invTileHeight = 1.0 / tileHeight;
			var canvasWidth = 180;
			var canvasHeight = 160;
			var canvas = null;
			var ctx = null;
			var camera = null;
			var vehicle = null;
			
			function loop() {
				// Tick
				vehicle.tick();
				camera.tick();
				
				// Render
				ctx.fillStyle = "gray";
				ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
				
				// Draw Background
				var invZoom = 1.0 / camera.zoom;
				var maxX = (canvasWidth >> 1) * invZoom;
				var maxY = (canvasHeight >> 1) * invZoom;
				var minX = ((-maxX + camera.x) * invTileWidth) | 0;
				var minY = ((-maxY + camera.y) * invTileHeight) | 0;
				maxX = ((maxX + camera.x) * invTileWidth) | 0;
				maxY = ((maxY + camera.y) * invTileHeight) | 0;
				
				ctx.rotate(-camera.angle);
				ctx.lineWidth = camera.zoom;
				ctx.strokeStyle = "white";
				ctx.beginPath();
				
				for (var x = minX - 2; x < maxX + 2; ++x) {
					for (var y = minY - 2; y < maxY + 2; ++y) {
						ctx.rect(
							(x * tileWidth - camera.x) * camera.zoom,
							(y * tileHeight - camera.y) * camera.zoom,
							tileWidth * camera.zoom,
							tileHeight * camera.zoom
						);
					}
				}
				
				ctx.stroke();
				
				vehicle.render(ctx,camera);
				ctx.rotate(camera.angle);
				//
				requestAnimationFrame(loop);
			}
			
			var toggle = false;
			
			onmousedown = function() {
				if (toggle = !toggle) {
					camera.track(vehicle,0.0,0.0,1.5);
				} else {
					camera.set(0.0,0.0,0.0,1.0);
				}
			}
			
			onload = function() {
				canvas = document.getElementById("canvas");
				canvas.width = canvasWidth;
				canvas.height = canvasHeight;
				
				ctx = canvas.getContext("2d");
				ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
				
				var path = [];
				
				for (var i = 0, l = (Math.random() * 25) | 0; i < l; ++i) {
					path.push(new Point(
						Math.random() * canvasWidth - canvasWidth * 0.5,
						Math.random() * canvasHeight - canvasHeight * 0.5
					));
				}
				
				camera = new Camera(0.0,0.0);
				vehicle = new Vehicle(0.0,0.0,path);
				
				loop();
			}
			
		}();
		
		</script>
	</body>
</html>


推荐阅读