Canvas Quirks

While using Canvas 2D context for drawing stuff I discovered that the drawing line API can surprise you a bit especially when drawing horizontal or vertical lines. Here is a screenshot with a Canvas element and 5 lines drawn using lineTo() calls:

In case you haven’t noticed, let me tell you what’s wrong with this: the lines are suposed to be 1 pixel width and black. Clearly what you see on the screen is not 1 pixel and the lines are somehow grayish. It looks more like 2 pixels. The code for drawing this looks like this:


<input onclick="draw()" type="button" value="draw" />

<script type="text/javascript">// <![CDATA[
function draw() {
    var context, i, y;

    context = document.getElementById('canvas').getContext('2d');
    y = 20;
    context.lineWidth = 1;
    context.strokeStyle = '#000000';
    for (i = 0; i < 5; i++) {
       context.moveTo(0, y);
       context.lineTo(450, y);
       y += 10;
    }
    context.stroke();
}
// ]]></script>

Let’s change the line width to 2 (line 10 in the above code snippet) and check the result:


Interesting, isn’t it? So the lines width is basically the same, but the color now is really black. Now, let’s try something else: change the line width back to 1 and adjust the y property of the moveTo/lineTo functions with o.5 (line 13/14):

context.moveTo(0, y + 0.5);
context.lineTo(450, y + 0.5);

And surprise, surprise the lines are now exactly 1 pixel and black:

So what’s happening? After some research I think that this is what is happening:

Using fillRect() function instead of lineTo()

If you don’t like adding those 0.5 to any coordinate when using the lineT0() API then you can actually use the drawing rectangle API. As you probably already guessed, the trick is to draw a rectangle of one pixel for one dimension and the length you need for the other one. So here is the script for drawing 5 horizontal lines:

function draw() {
    var context, i, y;

    context = document.getElementById('canvas').getContext('2d');
    y = 20;

    for (i = 0; i < 5; i++) {
       context.fillRect(0, 10 + y, 450, 1);
       y += 10;
    }
}

And here is the result:

If you are wondering about performance differences between lineTo() and fillRect() then you shouldn’t. fillRect() is probably even faster than lineTo().

You can see here a page that illustrates the differences between lineTo() and fillRect() when using integer coordinates.

Comments

Leave a Reply




Switch to our mobile site