Drawing a custom HTML5 shape

HTML5 is fun. I’ve meant to post about it more than I have in the past (which is 0 posts). I recently got the urge, so I want to show how to draw a heart and a star on HTML5 canvas.

Some background

A few days ago, I heard about typescript, which is a typed superset of JavaScript. It’s similar to coffeescript, dart, and others in that it defines a language which compiles down to plain JavaScript. It’s different than most because it gives JavaScript the concept of types and compile-time type checking. The compiler is available cross-platform, but the IDE tools are only available for Visual Studio 2012 or in-browser on the website.

Anyway, I wanted to play around with typescript so I decided to see how easily I could convert brushes.js to use some of the typed semantics of typescript. It wasn’t too difficult and the code actually looks way cleaner in typescript. The only issue I’ve found is that types are not inferred 100% accurately at times. For instance, document.getElementsByTagName returns a NodeList. Indexed elements are considered Node elements and not a more specific type. An example, you would expect the following snippet to compile properly:

if($tag) {
	var canvas = $tag('canvas') ? $tag('canvas')[0] : null;
	if(canvas && canvas.getContext) {
		this.context = canvas.getContext("2d");
	}
}

Unfortunately, you’re presented with a message that says getContext doesn’t exist on Node.
typescript Node select message

To remove this message, you need to do something I personally don’t like doing (although it’s completely legal in JavaScript)– access the function by key.

if($tag) {
	var canvas = $tag('canvas') ? $tag('canvas')[0] : null;
	if(canvas && canvas["getContext"] && typeof canvas["getContext"] === "function") {
		this.context = canvas["getContext"]("2d");
	}
}

Aside from this little problem, I found the coding experience to be beneficial and fun. In fact, while modifying some of the code for the brushes (hearts and stars), I was motivated to write a blog post showing how to create some basic custom shapes in HTML5…

HTML5

The awesome thing about HTML5 is that you now have a drawing area natively embedded directly into HTML. That’s pretty awesome because it opens up the browser to do some really awesome stuff. There are some pretty phenomenal examples of the power of HTML5 (sometimes mixed with WebGL) at chromeexperiments.com. In fact, I had the urge to start working on brushes.js after playing with an experiment called Harmony by MrDoob. MrDoob has since moved onto creating the hugely-popular three.js. With those two links, I think you can get lost for hours in the power of the browser. If you’re just getting started with some of the new HTML5 APIs, you may want to check out remy’s HTML5 demos.

Drawing

How to draw a star on xkcd.com
Image courtesy of xkcd.com

Drawing is nothing new in programming. You can easily find algorithms for drawing specific shapes on game dev sites. In fact, I believe I found the algorithm for the star in a C++ game programming book I purchased but haven’t gotten around to reading fully.

The HTML5 canvas is a grid in which (0,0) is in the top-let corner of the grid. It is easiest to draw shapes starting at the origin, then move them into place when you’re done. The canvas API makes this easy using the translate function. Suppose you want to draw an image at (100,100). Using context.translate(100,100) (where context is the 2d drawing context of your canvas element) will cause the drawing context to temporarily set the origin at (100,100), allowing you to draw easily without the headache of calculating offsets.

The steps necessary to draw a simple shape are as follows:

  1. Query the dom for the canvas element
  2. Call context.getContext(“2d”) on canvas element
  3. Save the context
  4. Translate context origin to desired location
  5. Set additional properties on the context
  6. Call context.beginPath()
  7. Draw your shapes
  8. Stroke or fill your shape
  9. Call context.closePath() to indicate drawing has completed
  10. Call context.restore() to restore the previously saved context

This may seem like quite a bit. For an example, I’ve created a jsfiddle so you can play around and see the above steps in action.

Try it

Code

The code for this blog post is available on github at jimschubert/blogs