23

I'm drawing line graphs on a canvas. The lines draw fine. The graph is scaled, every segment is drawn, color are ok, etc. My only problem is visually the line width varies. It's almost like the nib of a caligraphy pen. If the stroke is upward the line is thin, if the stroke is horizontal, the line is thicker.

My line thickness is constant, and my strokeStyle is set to black. I don't see any other properties of the canvas that affect such a varying line width but there must be.

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
simusid
  • 1,854
  • 3
  • 18
  • 26

4 Answers4

72

Javascript:

var badCanvas = document.getElementById("badCanvas"),
    goodCanvas = document.getElementById("goodCanvas"),
    bCtx = badCanvas.getContext("2d"),
    gCtx = goodCanvas.getContext("2d");

badCanvas.width = goodCanvas.width = badCanvas.height = goodCanvas.height = 300;

// Line example where the lines are blurry weird ect.
// Horizontal
bCtx.beginPath();
bCtx.moveTo(10,10);
bCtx.lineTo(200,10);
bCtx.stroke();
//Verticle
bCtx.beginPath();
bCtx.moveTo(30,30);
bCtx.lineTo(30,200);
bCtx.stroke();

// Proper way to draw them so they are "clear"
//Horizontal
gCtx.beginPath();
gCtx.moveTo(10.5,10.5);
gCtx.lineTo(200.5,10.5);
gCtx.stroke();
//Verticle
gCtx.beginPath();
gCtx.moveTo(30.5,30);
gCtx.lineTo(30.5,200);
gCtx.stroke();


// Change the line width
bCtx.lineWidth = 4;
gCtx.lineWidth = 4;


// Line example where the lines are blurry weird ect.
// Horizontal
bCtx.beginPath();  
bCtx.moveTo(10,20.5);
bCtx.lineTo(200,20.5);
bCtx.stroke();
//Verticle
bCtx.beginPath()
bCtx.moveTo(50.5,30);
bCtx.lineTo(50.5,200);
bCtx.stroke();

// Proper way to draw them so they are "clear"
//Horizontal
gCtx.beginPath();
gCtx.moveTo(10,20);
gCtx.lineTo(200,20);
gCtx.stroke();
//Verticle
gCtx.beginPath();
gCtx.moveTo(50,30);
gCtx.lineTo(50,200);
gCtx.stroke();

HTML:

<h2>BadCanvas</h2>
<canvas id="badCanvas"></canvas>
<h2>Good Canvas</h2>
<canvas id="goodCanvas"></canvas>

CSS:

canvas{border:1px solid  blue;}

Live Demo

My live demo basically just recreates what the MDN says. For even stroke widths you can use integers for coordinates, for odd stroke widths you want to use .5 to get crisp lines that fill the pixels correctly.

MDN Image

From MDN Article

If you consider a path from (3,1) to (3,5) with a line thickness of 1.0, you end up with the situation in the second image. The actual area to be filled (dark blue) only extends halfway into the pixels on either side of the path. An approximation of this has to be rendered, which means that those pixels being only partially shaded, and results in the entire area (the light blue and dark blue) being filled in with a color only half as dark as the actual stroke color. This is what happens with the 1.0 width line in the previous example code.

To fix this, you have to be very precise in your path creation. Knowing that a 1.0 width line will extend half a unit to either side of the path, creating the path from (3.5,1) to (3.5,5) results in the situation in the third image — the 1.0 line width ends up completely and precisely filling a single pixel vertical line.

user4035
  • 22,508
  • 11
  • 59
  • 94
Loktar
  • 34,764
  • 7
  • 90
  • 104
  • 3
    the live demo was much appreciated. I also found that if you specify the height of canvas in CSS it causes blurring. – LDK Apr 12 '12 at 01:19
  • @LDK yeah changing the height using CSS just scales it so it'll make everything pretty blurry. – Loktar Apr 12 '12 at 13:33
  • [Your live demo](http://jsfiddle.net/loktar/KcTfH/) had an error causing the 'good' horizontal 1px line to have blurred ends. I have corrected it to [this](http://jsfiddle.net/KcTfH/38/). Best to get it right when teaching others. – James Haigh Dec 12 '12 at 22:51
  • Thanks to [yet another ridiculous SO rule](http://meta.stackexchange.com/questions/149890/prevent-posts-with-links-to-jsfiddle-and-no-code), I could not submit my edit without adding that dummy code line. Please remove it if possible. – James Haigh Dec 12 '12 at 23:08
  • @JamesHaigh I appreciate the edit, but it wasn't needed if you look at the actual rendered differences in Chrome and Firefox there was no visible change. – Loktar Dec 13 '12 at 13:18
  • @Loktar: The change ***is*** visible, albeit small. Reverting the edit was pointless though, because the edited version is more correct than your version. – James Haigh Dec 15 '12 at 00:01
  • @Loktar: BTW, I tried it in both Firefox and Chromium, and the correction is visible in both. – James Haigh Dec 15 '12 at 00:08
  • Link to article is broken. I couldn't fix the error without having to put a code sample. The S.O. rule makes sense, you should put a concise code example. What if somebody in the future wants to understand what's about and JSFiddle is not online anymore. Anyway. Here's link to the article https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors – renoirb Oct 08 '15 at 01:57
  • @renoirb thanks for the link. This answer was before SO offered snippets. I'll have to update it at some point. – Loktar Oct 08 '15 at 03:01
2

If linewidth is an odd number, just add 0.5 to x or y.

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
hzqij1978
  • 263
  • 2
  • 9
1

I just solved a problem of a similar nature. It involved a bug in a For loop.

PROBLEM: I had created a for loop to create a series of connected line segments and noticed that the line was thick to start but thinned out significantly by the final segment (no gradients were explicitly used).

FIRST, DEAD END THOUGHT: At first I assumed it was the above pixel issue, but the problem persisted even after forcing all the segments to remain at a constant level.

OBSERVATION: I noticed that I made a newbie's mistake -- I only used a single "ctx.beginPath()" and "ctx.moveTo(posX,posY)" PRIOR to the For loop and a single "ctx.stroke()" AFTER the For loop and the loop itself wrapped a single ctx.lineTo().

SOLUTION: Once I moved all methods (.beginPath(), .moveTo(), .lineTo() and .stroke()) together into the For loop so they would all be hit on each iteration, the problem went away. My connected line had the desired uniform thickness.

JAM
  • 19
  • 1
0

Try lineCap = "round" and lineJoin = "round". See "Line Styles" in this PDF to see what these parameters do.


Edit 17-July-2015: Great cheat sheet, but the link is dead. As far as I can tell, there's a copy of it at http://www.cheat-sheets.org/saved-copy/HTML5_Canvas_Cheat_Sheet.pdf.

Ed Gibbs
  • 25,924
  • 4
  • 46
  • 69
Septnuits
  • 4,804
  • 1
  • 14
  • 7
  • Tried and did not make a difference. I believe the issue is the even/odd stroke width comment below. Thanks – simusid Sep 23 '11 at 18:01