I'm trying to create a grid with a canvas library. I'm using React PixiJS library but I am sure this is not related with PixiJS or React. Because same thing happens when I use standard HTML canvas and pure javascript.
I'm trying to draw a 100x100 grid and I'm doing that with 200 straight lines.
My code to draw 100x100 grid as follows:
const lineWidth = 0.5;
const lineColor = 0x000000;
g.lineStyle(lineWidth, lineColor);
// Draw horizontal lines
for (var x = 0; x <= 1000; x += 10) {
g.moveTo(x, 0);
g.lineTo(x, 1000);
}
// Draw vertical lines
for (var y = 0; 1000; y += 10) {
g.moveTo(0, y);
g.lineTo(1000, y); // OK
}
In higher resolution devices it shows grid perfectly as it should but in lower resolutions or sometimes in higher resolutions aswell, it skips some lines. It actually draws it because when I zoom in I can see they are rendered but initially I can't see it.
Below are some examples to explain what does "skipping lines" mean:
To get the best results rendering to the canvas ensure that the canvas resolution matches the canvas size.
That is the canvas.width
and height
should match the canvas.style.width
and canvas.style.height
You also need to align the render calls to the pixels. The 2D renderer will anti alias content which can create artifacts if you do not align horizontal and vertical lines to pixels.
Lastly ensure that the grid spacing in a whole number.
This shows what happens when you do not match the canvas display size to the canvas resolution and do not align the rendering to pixels.
const ctx = canvas.getContext("2d");
const styles = {
thinLine: { lineWidth: 0.25, strokeStyle: "#000" },
pxLine: { lineWidth: 1, strokeStyle: "#000" },
};
function line(x1,y1,x2,y2) {
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
}
function ctxStyle(style) { Object.assign(ctx, style); }
function drawGrid(spacing) {
var p = 0;
const W = canvas.width, H = canvas.height, size = Math.max(W, H);
ctx.beginPath();
while (p < size) {
p <= W && line(p, 0, p, H);
p <= H && line(0, p, W, p);
p += spacing;
}
ctx.stroke();
}
ctxStyle(styles.thinLine);
drawGrid(1000 / 30);
ctxStyle(styles.pxLine);
drawGrid(1000 / 5);
canvas {
width: 333px;
height: 333px;
}
<canvas id="canvas" width="1000" height="1000"></camvas>
This example sets the canvas resolution to match the display size which improves the display. However the lines are still inconsistent as there is no effort to align the lines with pixels.
const ctx = canvas.getContext("2d");
const styles = {
thinLine: { lineWidth: 0.25, strokeStyle: "#000" },
pxLine: { lineWidth: 1, strokeStyle: "#000" },
};
function line(x1,y1,x2,y2) {
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
}
function ctxStyle(style) { Object.assign(ctx, style); }
function drawGrid(spacing) {
var p = 0;
const W = canvas.width, H = canvas.height, size = Math.max(W, H);
ctx.beginPath();
while (p < size) {
p <= W && line(p, 0, p, H);
p <= H && line(0, p, W, p);
p += spacing;
}
ctx.stroke();
}
ctxStyle(styles.thinLine);
drawGrid(333 / 30);
ctxStyle(styles.pxLine);
drawGrid(333 / 5);
canvas {
width: 333px;
height: 333px;
}
<canvas id="canvas" width="333" height="333"></canvas>
This example sets the canvas resolution to match the display size and align the lines to pixels.
Now the lines are consistent, however close inspection will show that not all grid lines are the same distance apart.
const ctx = canvas.getContext("2d");
const styles = {
thinLine: { lineWidth: 0.25, strokeStyle: "#000" },
pxLine: { lineWidth: 1, strokeStyle: "#000" },
};
function line(x1,y1,x2,y2) {
ctx.moveTo((x1 | 0) + 0.5, (y1 | 0) + 0.5);
ctx.lineTo((x2 | 0) + 0.5, (y2 | 0) + 0.5);
}
function ctxStyle(style) { Object.assign(ctx, style); }
function drawGrid(spacing) {
var p = 0;
const W = canvas.width, H = canvas.height, size = Math.max(W, H);
ctx.beginPath();
while (p < size) {
p <= W && line(p, 0, p, H);
p <= H && line(0, p, W, p);
p += spacing;
}
ctx.stroke();
}
ctxStyle(styles.thinLine);
drawGrid(333 / 30);
ctxStyle(styles.pxLine);
drawGrid(333 / 5);
canvas {
width: 333px;
height: 333px;
}
<canvas id="canvas" width="333" height="333"></canvas>
For the best result ensure that the canvas resolution is divisible by the grid spacing (a whole number (integer)). Ie dividing canvas.width by grid spacing should give you a whole number. The above example had a canvas 333 by 333 and 30 grid lines. The spacing was 11.1px. The example below is 330 by 330 with 30 grid lines, the spacing is 11px
const ctx = canvas.getContext("2d");
const styles = {
thinLine: { lineWidth: 0.25, strokeStyle: "#000" },
pxLine: { lineWidth: 1, strokeStyle: "#000" },
};
function line(x1,y1,x2,y2) {
ctx.moveTo((x1 | 0) + 0.5, (y1 | 0) + 0.5);
ctx.lineTo((x2 | 0) + 0.5, (y2 | 0) + 0.5);
}
function ctxStyle(style) { Object.assign(ctx, style); }
function drawGrid(spacing) {
var p = 0;
const W = canvas.width, H = canvas.height, size = Math.max(W, H);
ctx.beginPath();
while (p < size) {
p <= W && line(p, 0, p, H);
p <= H && line(0, p, W, p);
p += spacing;
}
ctx.stroke();
}
ctxStyle(styles.thinLine);
drawGrid(333 / 30);
ctxStyle(styles.pxLine);
drawGrid(333 / 5);
canvas {
width: 330px;
height: 330px;
}
<canvas id="canvas" width="330" height="330"></canvas>