Home > other >  Horizonal and vertical lines drawn on HTML5 Canvas are two pixels wide, why?
Horizonal and vertical lines drawn on HTML5 Canvas are two pixels wide, why?

Time:10-06

When drawing horizontal and vertical lines on a HTML canvas I cannot get the output to render lines 1 pixel wide.

I have been through a number of solutions including offsetting coordinates by 0.5, translating by 0.5, multiplying the canvas width/height by the devicePixelRatio, even grabbing the canvas image and directly manipulating the pixel data.

On High DIP screens - such as my Surface Pro - offsetting by 0.5 pixels and using the devicePixelRatio to scale and size the canvas works, I get a nice crisp 1px width line, but on my standard 1920 x 1080 monitors (devicePixelRatio == 1) it reverts back to 2px width.

I understand this can be caused by drawing against pixel boundaries, which is what the 0.5 offset is supposed to help with in some scenarios, but this hasn't helped in any of the tests I have done.

This image shows what I see on my monitors

Here is my codepen I've been playing with. It's the project I took the above image from.

For completeness I have included the js I've been working with below.

const canvas = document.getElementById("canvas");

const dpi = window.devicePixelRatio;

const rect = canvas.getBoundingClientRect();

canvas.width = rect.width * dpi;
canvas.height = rect.height * dpi;

canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;

const rowHeight = 25;
const colWidth = 100;

const ctx = canvas.getContext("2d");

ctx.scale(dpi,dpi);

ctx.beginPath();

ctx.strokeStyle = '#bdbdbd';

for(let i = rowHeight   0.5; i < canvas.height; i  = rowHeight){
  
  ctx.moveTo(0,i);
  ctx.lineTo(canvas.width,i);
  
}

for(let i = colWidth   0.5; i < canvas.width; i  = colWidth){
  
  ctx.moveTo(i,0);
  ctx.lineTo(i,canvas.height);
  
}

ctx.stroke();

This is another one of my failed attempts applying a SVG to the canvas as an image, this had some interesting results.

I've been looking at other projects which achieve the desired results such as jassmith's Glide-Data-Grid (his demos work without issue on my pc monitors) but I've been unable to work out what I'm doing wrong, unless there is something in the call chain I'm missing, his grid draw routine (src/data-grid/data-grid-render drawGridLines() line 448) looks rather standard.

CodePudding user response:

The issue is certainly caused by the CSS box-sizing: border-box rule. When you set this rule, the CSS width and height values will also take the border-width in that size. This means that if e.g you have a 125x125px bitmap canvas, that you do resize to 500x500px through CSS with a 1px border, your bitmap will actually be stretched to (500-2) x (500-2)px. And scaling from 125 to 498 will not correspond to the scale of your monitor's DPI, hence there will be antialiasing when rendering your bitmap. This is probably even more noticeable with a x1 monitor since there you'll always go from original-size to original-size - 2px, but even on a x2 monitor you may face that issue.

So remove that rule and you should be fine.

  • Related