DEV Community

David Miller
David Miller

Posted on

Set the canvas size before configuring the drawing context

If a canvas loses its background on the second render, inspect its size assignments. Look for writes to canvas.width and canvas.height between painting the background and drawing the image.

Assigning either dimension resets the drawing context. The pixels disappear, and settings such as the fill color, transform, and compositing mode return to their defaults. Even assigning the width its existing value causes a reset. MDN documents this behavior in its canvas width reference.

The order that loses the background

Suppose an export should place a transparent image on white. This sequence contains a problem:

const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
canvas.width = targetWidth;
canvas.height = targetHeight;
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
Enter fullscreen mode Exit fullscreen mode

The background was painted into the old bitmap. Changing the dimensions clears it before the image is drawn. A preview against a white page may conceal the mistake because the page shows through the transparent canvas.

Set both dimensions first, then establish the drawing state:

canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("A 2D context is required");
ctx.fillStyle = "white";
ctx.fillRect(0, 0, targetWidth, targetHeight);
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
Enter fullscreen mode Exit fullscreen mode

Here image must already be decoded, and the target dimensions must be positive integers chosen by the application. The example assumes their aspect ratio is intentional. Stretching a photograph into a different ratio would be a separate bug.

Make each render complete

Give the render function the source image and the output settings it needs. After setting the dimensions, apply every setting the drawing operation depends on. That makes the second render follow the same path as the first.

For example, if the application uses a transform for rotation, configure that transform after resizing. If it disables smoothing for pixel art, apply that choice after resizing too. A context object remaining in a variable does not mean its earlier settings survived.

Avoid using canvas.width = canvas.width as an unexplained clearing trick. It also resets state, which can make later edits difficult to reason about. If clearing pixels is the intention, a deliberate clearing operation communicates that intention more clearly, with the current transform and clipping region taken into account.

A small regression case

Use a transparent test image with an opaque colored square in the middle. Render it onto a white background, change the output dimensions, then render it again. Inspect a corner that should contain only the background. The corner should remain white after both renders.

Also exercise the case where the second render requests the same dimensions. A test that only checks changes in size can miss an unconditional width assignment in a shared helper. Keep the test focused on the output pixels and the behavior the user needs, rather than asserting which internal helper ran.

When a preview looks correct but an exported file has an unexpected background, inspect the drawing sequence before adjusting compression. An encoder cannot restore a background that was cleared before encoding began. Put both size assignments near the start of the render function so a later edit does not clear work already drawn.

Top comments (0)