0

I'm trying out canvas and I can't get the lines to get thinner.

I'm using lineWidth as .5 but it's still too bold.

const c = canvas.getContext('2d');

canvas.width = this.track.width();
canvas.height = 18;

c.beginPath()
c.moveTo(where, 20)
c.lineTo(where, 20)
c.lineTo(where, 0)

// If i go .4 the line starts having opacity
c.lineWidth = .5;
c.stroke()

Is there no solution?

This is how my grid looks like

my grid

Leonardo Bezerra
  • 668
  • 1
  • 10
  • 20

3 Answers3

2

Opacity is how a canvas creates the appearance of lines narrower than a pixel, because a pixel is (by definition) the smallest unit a canvas works with.

That doesn't mean all hope is lost, however, because this is only a problem depending on what you mean by "pixel".

There's the CSS unit px, which corresponds to what used to be the smallest actual pixels available on video display devices, typically in the range of 72-96 per inch (28-38 per cm).

And then there are actual device pixels, which are now often half as small or smaller.

The trick to getting sharper lines with a canvas when you've got high-res device pixels is scaling. Use this to figure how much you can effectively scale:

const scaling = window.devicePixelRatio || 1;

Suppose the value here is 2 (as it is on my current laptop). If you want to create a canvas that occupies, say, 400px by 300px, create an 800x600 canvas. Then use CSS styling width: 400px; height: 300px on your canvas.

Now you'll be able to create sharp half-width lines so long as the device you're drawing to supports the higher resolution.

I use this trick many places in the Angular app at https://skyviewcafe.com/. There's a link there to the source code if you want to dig through it to find examples of high-res canvas drawing.

Please note!

You're either have to specify a lineWidth of 1 when you mean half-width, or use canvas scaling like this:

const context = this.canvas.getContext('2d');
context.scale(scaling, scaling);

Be careful with context.scale() — its effects are cumulative. If you execute context.scale(2, 2) twice in a row with the same context, your scaling factor will be 4, not 2.

context.setTransform(1, 0, 0, 1, 0, 0);

...resets the scaling factor if you want to call context.scale() more than once, without the effect being cumulative.

kshetline
  • 12,547
  • 4
  • 37
  • 73
  • This really did get me a thinner look, but it's still kind of bold even so – Leonardo Bezerra Aug 19 '19 at 13:25
  • Here's some extra help in understanding line drawing with a canvas: https://mobtowers.com/2013/04/15/html5-canvas-crisp-lines-every-time/. There's always going to be anti-aliasing on slanted lines, but with care horizontal and vertical lines can be pixel perfect. – kshetline Aug 19 '19 at 13:53
  • just another question, does context.scale work as a zoom maybe? – Leonardo Bezerra Aug 20 '19 at 02:03
0

Try this..

canvas.width = this.track.width;
canvas.height = 18;    

const c = canvas.getContext('2d');
c.beginPath();
c.moveTo(0, 0);
c.lineTo(canvas.width, 18);
c.lineWidth = 0.5;
c.stroke();
Alwin Jose
  • 696
  • 1
  • 5
  • 13
0

The canvas is more or less just an image -> bitmap you can't draw half a pixel for your line on that bitmap.

What you can do however is to double the canvas size with canvas.width and canvas.height and scale it back down with CSS or the HTML tag width and height attribute.

Or use an alpha value like context.strokeStyle = 'rgba(r, g, b, 0.5)'; that the line appears to have half the thickness.

Also keep in mind that the drawing routine draws lines in between 2 pixels, so adding 0.5 to non floating point values on each x and y coordinate will make the line appear smaller too.