will.institute

by Will Lesieutre

Canvas Resolution

In the first canvas demo, I showed a small canvas with its width and height attributes set to 200 pixels. What about a bigger canvas fitting the width of the page content?

Since the width of the content column will vary, setting the canvas element’s width attribute in pixels won’t work, I need a percentage size instead. CSS has this covered.

<canvas id="wideCanvas" style="width: 100%"></canvas>
This canvas's width fills the content column.

This time I’ve added an inner shadow to the canvases to better show where they are, but it’s otherwide blank until we call a function to draw something. Draw a diagonal line from the top left to bottom right:

function drawLine(color, canvasID) {
    const canvas = document.getElementById(canvasID);
    const ctx = canvas.getContext("2d");
    ctx.lineWidth = 10;
    ctx.strokeStyle = color;
    ctx.moveTo(0, 0);
    ctx.lineTo(canvas.width, canvas.height);
    ctx.stroke();
}

Pixel problems

The canvas’s size is as wide as the content column, but it’s very pixelated! What’s wrong with it?

The drawing works like an image. A canvas context has a specific size in pixels, and if the size it’s displayed at needs more pixels than that, the image gets stretched out to fill the space. It’s not like a vector graphic that can stay pixel sharp automatically as it grows.

But if the drawing context’s size doesn’t come from the displayed size of the canvas, where does it come from? It’s based on the width and height attributes of the canvas element. I didn’t set either of those on the canvas above, which leaves browsers to assume a default of 300 pixels wide by 150 pixels high. If your window is much bigger than that (and it probably is), this isn’t drawning at a large enough size to create a sharp image.

I’ll repeat the diagonal line demo, but this time the buttons first check the displayed size of the canvas, set its width and height attributes to match that, and once the size is correct they draw the same line from top left to bottom right:

This canvas's element size can be updated to match its CSS size

function resizedLine(color, canvasID) {
    const canvas = document.getElementById(canvasID);
    canvas.height = canvas.scrollWidth / 2;
    canvas.width = canvas.scrollWidth;
    drawLine(color, canvasID);
}

Don’t resize your window

This version looks sharper on big screens, but only if it was drawn when the canvas was already large. If you hit the draw button with a small canvas and then maximize your window (or for the mobile readers, change from portrait to landscape), you’ll see the same pixelation as before. The canvas needs to be redrawn after a rezise to fix that. It’s an improvement, but it’s not responsive to layout changes.

Another point to consider here is that the line’s width is defined in pixels. When the canvas gets bigger, the line is drawn longer to cover the whole diagonal. Since its width stays the same, it becomes proportionately narrower. This approach could be useful sometimes, but if the goal is to draw a consistent and sharp image regardless of pixel dimensions, the line width could be set as fraction of the canvas’s size.

I’ll address those two problems in the next pair of posts.