如何使用 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>