如何使用 canvas 制作无限网格?
How can I make infinite grid with canvas?
不错的示例网格可以在这个站点中:https://playgameoflife.com,在那里你可以
单击并围绕此网格移动。所以我想学习如何制作这样一个你可以继续前进的无限网格。
片段:https://jsfiddle.net/omar_red/wfsLuynd/1/
canvas = document.querySelector('.field');
ctx = canvas.getContext('2d');
canvas.width = window.screen.width;
canvas.height = window.screen.height;
for (let x = 0.5; x < canvas.width; x += 10) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
}
for (let y = 0.5; y < canvas.height; y += 10) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
}
ctx.strokeStyle = "#888";
ctx.stroke();
body {
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Infinite Grid</title>
</head>
<body>
<canvas class="field"></canvas>
</body>
</html>
您必须处理鼠标事件才能知道按下鼠标左键时光标移动了多少。
想法是将 canvas 的坐标系移动与光标移动相同的量。为避免在我们离开的一侧出现空白,将网格绘制为宽度和高度的 3 倍。这样一来,您就无法通过一次“拖动”操作到达网格的边缘。
然后,当按钮被释放时,将坐标系恢复到原来的状态。所以实际上你撤消了整个动作。这对用户来说并不明显,他们的印象是网格只是停止移动并捕捉到一个不错的位置。
如果您的“世界”中有真实内容(如 Conway 的单元格),那么您将需要跟踪您的世界坐标移动了多少,当然,那些不会翻转回原始状态。要填充网格中的单元格,您需要将世界坐标映射到网格坐标。我没有在这里讨论这方面的内容,因为那样会离你的问题太远。
所以在下面的实现中只有网格在移动,但没有世界坐标或世界内容的概念:
let canvas = document.querySelector('.field');
let ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function draw() {
let step = 10;
let left = 0.5 - Math.ceil(canvas.width / step) * step;
let top = 0.5 - Math.ceil(canvas.height / step) * step;
let right = 2*canvas.width;
let bottom = 2*canvas.height;
ctx.clearRect(left, top, right - left, bottom - top);
ctx.beginPath();
for (let x = left; x < right; x += step) {
ctx.moveTo(x, top);
ctx.lineTo(x, bottom);
}
for (let y = top; y < bottom; y += step) {
ctx.moveTo(left, y);
ctx.lineTo(right, y);
}
ctx.strokeStyle = "#888";
ctx.stroke();
}
// Mouse event handling:
let start;
const getPos = (e) => ({
x: e.clientX - canvas.offsetLeft,
y: e.clientY - canvas.offsetTop
});
const reset = () => {
start = null;
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset translation
draw();
}
canvas.addEventListener("mousedown", e => {
reset();
start = getPos(e)
});
canvas.addEventListener("mouseup", reset);
canvas.addEventListener("mouseleave", reset);
canvas.addEventListener("mousemove", e => {
// Only move the grid when we registered a mousedown event
if (!start) return;
let pos = getPos(e);
// Move coordinate system in the same way as the cursor
ctx.translate(pos.x - start.x, pos.y - start.y);
draw();
start = pos;
});
draw(); // on page load
body, html {
width: 100%;
height: 100%;
margin: 0;
padding:0;
overflow:hidden;
}
canvas { background: silver; }
<canvas class="field"></canvas>
不错的示例网格可以在这个站点中:https://playgameoflife.com,在那里你可以 单击并围绕此网格移动。所以我想学习如何制作这样一个你可以继续前进的无限网格。
片段:https://jsfiddle.net/omar_red/wfsLuynd/1/
canvas = document.querySelector('.field');
ctx = canvas.getContext('2d');
canvas.width = window.screen.width;
canvas.height = window.screen.height;
for (let x = 0.5; x < canvas.width; x += 10) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
}
for (let y = 0.5; y < canvas.height; y += 10) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
}
ctx.strokeStyle = "#888";
ctx.stroke();
body {
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Infinite Grid</title>
</head>
<body>
<canvas class="field"></canvas>
</body>
</html>
您必须处理鼠标事件才能知道按下鼠标左键时光标移动了多少。
想法是将 canvas 的坐标系移动与光标移动相同的量。为避免在我们离开的一侧出现空白,将网格绘制为宽度和高度的 3 倍。这样一来,您就无法通过一次“拖动”操作到达网格的边缘。
然后,当按钮被释放时,将坐标系恢复到原来的状态。所以实际上你撤消了整个动作。这对用户来说并不明显,他们的印象是网格只是停止移动并捕捉到一个不错的位置。
如果您的“世界”中有真实内容(如 Conway 的单元格),那么您将需要跟踪您的世界坐标移动了多少,当然,那些不会翻转回原始状态。要填充网格中的单元格,您需要将世界坐标映射到网格坐标。我没有在这里讨论这方面的内容,因为那样会离你的问题太远。
所以在下面的实现中只有网格在移动,但没有世界坐标或世界内容的概念:
let canvas = document.querySelector('.field');
let ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function draw() {
let step = 10;
let left = 0.5 - Math.ceil(canvas.width / step) * step;
let top = 0.5 - Math.ceil(canvas.height / step) * step;
let right = 2*canvas.width;
let bottom = 2*canvas.height;
ctx.clearRect(left, top, right - left, bottom - top);
ctx.beginPath();
for (let x = left; x < right; x += step) {
ctx.moveTo(x, top);
ctx.lineTo(x, bottom);
}
for (let y = top; y < bottom; y += step) {
ctx.moveTo(left, y);
ctx.lineTo(right, y);
}
ctx.strokeStyle = "#888";
ctx.stroke();
}
// Mouse event handling:
let start;
const getPos = (e) => ({
x: e.clientX - canvas.offsetLeft,
y: e.clientY - canvas.offsetTop
});
const reset = () => {
start = null;
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset translation
draw();
}
canvas.addEventListener("mousedown", e => {
reset();
start = getPos(e)
});
canvas.addEventListener("mouseup", reset);
canvas.addEventListener("mouseleave", reset);
canvas.addEventListener("mousemove", e => {
// Only move the grid when we registered a mousedown event
if (!start) return;
let pos = getPos(e);
// Move coordinate system in the same way as the cursor
ctx.translate(pos.x - start.x, pos.y - start.y);
draw();
start = pos;
});
draw(); // on page load
body, html {
width: 100%;
height: 100%;
margin: 0;
padding:0;
overflow:hidden;
}
canvas { background: silver; }
<canvas class="field"></canvas>