How to draw opaque lines on an html canvas?

Viewed 13

Ive been working with an html canvas and after doing some drawing using lines, I notice that the background image is showing through.

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var x=0;
while(x <= 256) {
  ctx.beginPath();
  ctx.moveTo(x, 0);
  ctx.lineTo(x, 256);
  ctx.stroke();
  x++;
}
body {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpSoVh3YQcchQnSxIFXHUKhShQqgVWnUwufQLmhiSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxdHJSdJES/5cUWsR4cNyPd/ced+8AoVFlmtU1Dmi6bWZSSTGXXxFDr+hFCGFEkJCZZcxKUhq+4+seAb7exXmW/7k/R79asBgQEIlnmGHaxOvEU5u2wXmfOMrKskp8Tjxm0gWJH7muePzGueSywDOjZjYzRxwlFksdrHQwK5sa8SRxTNV0yhdyHquctzhr1Rpr3ZO/MFzQl5e4TnMYKSxgERJEKKihgipsxGnVSbGQof2kj3/I9UvkUshVASPHPDagQXb94H/wu1urOJHwksJJoPvFcT5GgNAu0Kw7zvex4zRPgOAzcKW3/RsNYPqT9Hpbix0BA9vAxXVbU/aAyx1g8MmQTdmVgjSFYhF4P6NvygORW6Bv1euttY/TByBLXaVvgINDYLRE2Ws+7+7p7O3fM63+fgBhUnKgA0cniQAAAAZiS0dEAMwAzADM38FfGgAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+YJBw4lNF/XAa8AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAg0lEQVQ4y63Uyw2AIBAE0HE62P5rowZK0JOG4ML+4ERk5mkIeLXWbhij9w4AEBErCnqxeZ4CNcBCGcE8a4wWrAwz2C7LLLbqsIJpXVaxGeUJbESJg0NEQM918mLfHlbRsU/tYRb7ncMoquXpCXmx5V220N06oyXrZYx8yZE/9ot49/YB4Ag88RP6jmkAAAAASUVORK5CYII=");
}
canvas {
  margin: 8 px;
  border: 1px solid red;
}
<canvas id="myCanvas" width="256" height=256 />

I tried various setting for stroke style and transparency settings, and checked for anti-aliasing problems, but to no luck at all.

Im using firefox 101.0.1 on ubuntu 20.04

1 Answers

This issue is caused by anti-aliasing being applied to your lines.

Let's have a look at a simple example - just a single straight line width the default lineWidth of 1.0 pixels and a strokeStyle of black going from x=128|y=0 to x=128|y=256.

If we utilize the CanvasRenderingContext2D's getImageData() method to obtain the actual RGBA color values at x=128 and x=127 respectively, we can see that instead of one time (0,0,0,255) - 255 being fully opaque - we get two times (0,0,0,127) instead. That means our single one pixel line became a two pixel semi-transparent rectangle.

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.strokeStyle = "#000000";
ctx.lineWidth = 1.0;
ctx.beginPath();
ctx.moveTo(128, 0);
ctx.lineTo(128, 256);
ctx.stroke();

console.log(ctx.getImageData(127, 0, 1, 1), ctx.getImageData(128, 0, 1, 1));
<canvas id="myCanvas" width="256" height=256 />

The only way to workaround this is offsetting your lines by 0.5 pixels.

For example:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var x = 0.5;
while (x <= 256) {
  ctx.beginPath();
  ctx.moveTo(x, 0);
  ctx.lineTo(x, 256);
  ctx.stroke();
  x++;
}
body {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpSoVh3YQcchQnSxIFXHUKhShQqgVWnUwufQLmhiSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxdHJSdJES/5cUWsR4cNyPd/ced+8AoVFlmtU1Dmi6bWZSSTGXXxFDr+hFCGFEkJCZZcxKUhq+4+seAb7exXmW/7k/R79asBgQEIlnmGHaxOvEU5u2wXmfOMrKskp8Tjxm0gWJH7muePzGueSywDOjZjYzRxwlFksdrHQwK5sa8SRxTNV0yhdyHquctzhr1Rpr3ZO/MFzQl5e4TnMYKSxgERJEKKihgipsxGnVSbGQof2kj3/I9UvkUshVASPHPDagQXb94H/wu1urOJHwksJJoPvFcT5GgNAu0Kw7zvex4zRPgOAzcKW3/RsNYPqT9Hpbix0BA9vAxXVbU/aAyx1g8MmQTdmVgjSFYhF4P6NvygORW6Bv1euttY/TByBLXaVvgINDYLRE2Ws+7+7p7O3fM63+fgBhUnKgA0cniQAAAAZiS0dEAMwAzADM38FfGgAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+YJBw4lNF/XAa8AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAg0lEQVQ4y63Uyw2AIBAE0HE62P5rowZK0JOG4ML+4ERk5mkIeLXWbhij9w4AEBErCnqxeZ4CNcBCGcE8a4wWrAwz2C7LLLbqsIJpXVaxGeUJbESJg0NEQM918mLfHlbRsU/tYRb7ncMoquXpCXmx5V220N06oyXrZYx8yZE/9ot49/YB4Ag88RP6jmkAAAAASUVORK5CYII=");
}

canvas {
  margin: 8 px;
  border: 1px solid red;
}
<canvas id="myCanvas" width="256" height=256 />

Related