javascript - 尝试创建 HTML Canvas Tiles 并通过对象移动使其动态化
问题描述
尝试用图块创建一个画布,如图所示,尝试向各个方向移动车辆对象,是否有实现此功能的解决方案?
功能:每当车辆对象在网格上移动时,网格应根据车辆移动而旋转,类似于谷歌地图的功能。
谁能给我解决方案。
解决方案
如果您使用偏移量来绘制所有对象并执行一些屏幕外剔除,则“无限”画布很容易。
通过单击画布在视图之间切换。
<!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>
推荐阅读
- javascript - 具有三层组件的 ReactJS 中状态的正确位置
- c# - 使用 c# 在 DataGridView 中仅遍历 1 列
- vb.net - 如何在 vb.net 中保存用户输入
- oracle - 如何使用 SQLPLUS 直接连接到 Oracle 数据库链接
- excel - Excel - 从单列中获取年份范围
- ios - 从“数据”强制转换?to 'Data' 只解开可选项;你的意思是使用'!'?
- python - Python 中的通用 3D 体积图
- google-chrome - Chrome 上的 PowerApps 登录问题
- arrays - 如何在swift 4中计算数组的所有模式?
- node.js - 如何使用 nodejs 和 mongoose 从 mongodb 集合中删除数据