Monday, June 4, 2012

What I learned about the canvas element

So, I am apparently a sucker for challenges. A couple of weeks ago, my friends at the Des Moines Web Geeks issued the challenge to re-build in Javascript a simple dungeon game, which one of the organizers had built with his son in Python. I was unable to be at the coding dojo itself, but I joined in via Google Hangouts. And for some reason, I was hooked on the challenge.

But it was more than just the notion of rebuilding this simple game. The idea behind the whole thing was to teach someone how to code, a prospect which I have long thought to be a distinct challenge for our times. And we were supposed to use OOP JS as much as possible, something which I really enjoy. And then there was the fact that, beyond the basic parameters of the game, the concept was left wide open, which immediately got my imagination going.

So I decided from the very start that this little game, for me, would have two primary objectives:

  • I would learn something
  • Someone else would be able to learn something

For the thing that I would learn, I decided I would explore canvas to build a dashboard of sorts to display at a glance the status of the current player, the monster in the room, and a map of the world. Namely, since I had never before utilized the HTML5 canvas element in any project, I wanted to learn some of the basics and then explore just what the thing could do. I was incredibly pleased with what I came up with, and I wanted to share just a couple of quick lessons that I learned in the process.

The first lesson has to do with the canvas element itself. When I first started using canvas, I was simply putting the basic element into my HTML and then setting width and height with CSS. At first, things looked great. But as soon as I started drawing in the canvas, I realized that something was messed up. The images were distorted somehow. For example, a 5px vertical stroke was thicker than a 5px horizontal stroke. And then I noticed that filled rectangles that should have filled the entire canvas were all messed up. And I was compelled to realize that, somehow, there was a discrepancy between the internal dimensions of the canvas (i.e., how many pixels it thought it had) and the external dimensions of the canvas (i.e., how many pixels the page thought it had).

Sadly, it took me quite some time to find a tutorial or other documentation which acknowledged what I thought must have been an issue. But it wasn't an issue. Rather, it was a design feature of the canvas element.

<sarcasm>Imagine my surprise.</sarcasm>

So here's the deal. The canvas element's height and width attributes (i.e., the height="xxx" width="yyy" bits you put in the HTML) set the dimensions of the drawing space within the canvas element. While the CSS height and width rules set the dimensions of the element in relation to the rest of the page. The thing is, those two things don't necessarily correspond.

In fact, the canvas element, regardless of its size in the CSS, will have a drawing space of 300 pixels wide and 150 pixels in height. Unless, that is, you specify otherwise. The question is, how do you do that? Well, there are two options.

The first option is to specify it in the markup. Namely, by providing the height and width attributes in the opening tag, like this:

<canvas id="myCanvas" width="1000" height="1000" />

That's not so difficult. But what if you want the canvas drawing space to be exactly the same as the canvas element's actual dimensions on the page? And what if the actual dimensions are given in a percentage or may change? Try something like this in your JS onload callback (or onresize, etc.):

var el = document.getElementById('myCanvas');
el.setAttribute('width', el.offsetWidth);
el.setAttribute('height', el.offsetHeight);

And there you have it. One of the things I learned about canvas elements while playing with this simple little game.

i love my wife

No comments: