HTML 5 Canvas Stroke Coordinates
So HTML 5 has been getting a lot of attention recently — in no small part thanks to Google and others. Indeed, reading some of the features planned for HTML 5 are very exciting from a web developer’s perspective. The video and audio elements for standardized web page multimedia, worker threads and of course the canvas element and API.
Since getting excited about HTML 5, it’s been the canvas element that has most immediately grabbed my attention — both for the possibility it opens for a standardized way to render custom user interface components in HTML and for the fact it’s already quite well supported in the more popular alternative browsers (Firefox, Opera, Safari). So instead of studying maths like I should be doing (exams in less than two weeks!), I’ve been tinkering. Nothing exciting except to say that it’s fantastic to be able to render arbitrary graphics without waiting for an applet or Flash to load up.
However, I’ve been bitten by my first quirk in the API — a difference between how coordinates are used for strokes vs fills. Let me explain.
To draw a line using the HTML 5 canvas element, you use code that looks something like this:
`
var context = $(“canvas”).getContext(“2d”); context.strokeStyle = “#a00”; context.beginPath(); context.moveTo(5, 5); context.lineTo(100, 5); context.stroke();
`
The resulting line looks like this:
Hmm. The line appears to be two pixels wide. Why is that? I figure maybe it was presumptious of me to assume a default of 1 pixel for lines, so I checked the spec. Nope, the spec says the default is 1.0. I read a little about coordinate space vs. bitmap size, it still didn’t really explain the behaviour I was seeing. So I poked around on #whatwg, and was told this:
Philip
**: thomaslee: Integer coordinates refer to the positions \*between\* pixels **Philip
: thomaslee: If you want to draw a line through just a single column of pixels, you have to shift the coordinates by 0.5
snip
thomaslee: Philip: right, so that would explain why I’m still seeing two pixel lines when reducing line width. But why on earth do integer coordinates refer to the space in between pixels? **Philip
**: thomaslee: It’s that way so that fills work like you would expect (with sharp edges), but it has the consequence that strokes don’t quite work like you expect (so you have to shift them by 0.5 to the centers of pixels)
So I adjusted my code:
`
var context = $(“canvas”).getContext(“2d”); context.strokeStyle = “#a00”; context.beginPath(); context.moveTo(5, 5.5); context.lineTo(100, 5.5); context.stroke();
`
Sure enough, it fixed the issue:
Philip` then went on to explain why this counter-intuitive behaviour is to keep other aspects of the API suffering from similar issues. If you change the stroke behaviour, it breaks fills and vice versa. Without knowing the API for more than the few hours I’ve spent with it, I can’t really comment on it much more than that.
My brain hurts already.
Despite this weird behaviour, the canvas API on the whole seems to be very easy to follow and logical. And HTML 5 is slowly restoring my faith in the future of web development. We’re finally starting to see a move away from the hacks, work-arounds and the mish-mash of technologies that are the norm for modern web applications. Here’s hoping we don’t have to wait too long.
Now, back to the maths …