[This is part 6 of an ongoing series of posts examining the HTML5 Canvas element. In Part 1 of this series, I introduced Canvas and prepared a template to make further explorations a bit simpler, and also introduced JsFiddle, a neat tool for experimenting with and sharing web code. In Part 2, I demonstrated the ability of Canvas to allow your page background to shine through, and showed you how to render simple shapes on the drawing surface. In Part 3, I showed how to draw paths and text in Canvas. In Part 4, I showed how to transform the drawing context and scale, rotate, and skew your drawings. In Part 5, I introduced basic animation concepts, including the animation loop.]
The Part Where Things Get Messy
Now that you know how to get a shape moving across the screen, you’re probably wondering what’s next. Well, the truth is that the animation code we looked at in the previous installment of this series was fairly primitive, and if we tried to scale it to an animation or game with many shapes, we’d be writing a LOT of repetitive code, and we’d soon end up with a very messy batch of spaghetti.
Let’s look at an updated version of the sample from Part 5, which adds a few more properties to track the size of our shape, the direction of the animation (up, down, left, or right), the speed of the shape’s travel, etc. The updated example also includes 4 buttons for some rudimentary control of the direction of the animation (we’ll look at handling user input in greater depth in a future installment). Click the Result tab to see the code in action.
Spaghetti Recipe
With the additional variables, we have much greater control over our shape, but we have no fewer than 8 separate variables controlling how our shape looks and behaves. What happens if we want to have 3 of these shapes on-screen at once? Following the pattern above, we’d end up with 24 variables to manage. You can probably see already that this is not going to work as the number of shapes we want to animate increases, as in the image at right.
So what we need is a way to structure our animation code that reduces the need to repeat ourselves, and makes managing multiple animated shapes easier. The answer is to define our shape as a JavaScript object, like so:
1: function WokkaWokka(size, direction, speed, posX, posY) {
2: this.startAngle = 0.25;
3: this.endAngle = 1.75;
4: this.gapClosing = true;
5:
6: this.size = size;
7: this.direction = direction;
8: this.speed = speed;
9: this.posX = posX;
10: this.posY = posY;
11: };
In the above code, we’re creating what is in essence a class definition for our shape, which handles keeping track of all of the variables associated with that shape. To create an object from this definition, we can simply define a variable using the new keyword, passing in the desired values:
var WokkaWokkaMan = new WokkaWokka(50, right, 10, 25, 25);
This is fine so far, in that it solves the problem of managing variables related to the shape, but it still leaves open the question of how to manage multiple instances of our shape. For example, we can replicate the first example in this post with our new object template like so (click the Result tab to see the code in action):
A Better Solution – Objects Plus Arrays
This works fine for one shape, but if we add a second shape, it won’t animate, since all of the functions we use to render our animated shape refer to the first instance of the shape. What we need is a container that allows us to define and manage multiple instances of our shape, and to iterate over all of the shapes so that we can render each of them. Thankfully, JavaScript provides a simple means for doing this…an array. We can define an array and start adding instances in a pretty succinct fashion:
1: var WokkaWokkas = new Array();
2:
3: WokkaWokkas.push(new WokkaWokka(50, right, 10, 25, 25));
4: WokkaWokkas.push(new WokkaWokka(25, up, 5, 250, 25));
All that’s left is a bit of refactoring of our various calculation and rendering methods to include the current shape as an argument, and a for loop in our animation callback to iterate over the shapes in the array, and we’re good to go (click the Result tab to see the code in action):
To keep things fairly simple, I’ve reassigned the button handling functions to only change the direction of the first shape in the array. As you can see by clicking the Result tab above, the updated code allows us to manage multiple instances of our shape with ease, and without the need for repetitive code.
Summary
When creating animated shapes for HTML5 Canvas, it’s not enough to just have your code manage a single shape. By implementing your shapes using objects, and managing those shapes using arrays, you can make your code more readable and maintainable, which will come in handy as you start adding more functionality, such as input, which we’ll look at in the next installment. And since all of the code related to the shape is encapsulated in the function that creates the object, any new properties or features you add will be shared among all the shapes created from that object definition.
I’d like provide a shout-out to Rob Hawkes, whose book Foundation HTML5 Canvas: For Games and Entertainment provides some great examples on this subject that helped me conceptually with this post.
If you found this useful, why not tell your friends? You can also subscribe to my RSS feed, and follow me on twitter for more frequent updates.
More parts in the series:
- Part 1 – Introduction
- Part 2 – Basic Shapes
- Part 3 – Paths and Text
- Part 4 – Transformations
- Part 5 – Basic Animation
- Part 6 – Managing Animated Shapes (this post)
- Part 5 – Basic Animation
- Part 4 – Transformations
- Part 3 – Paths and Text
- Part 2 – Basic Shapes
Comments
Comment by Larry Serflaten on 2012-06-01 09:26:00 +0000
Good job, keep going! And thanks for the book reccomendation, it looks like what I need to get up to speed on some interactive canvas games!
Comment by devhammer on 2012-06-01 09:34:00 +0000
Glad you’re enjoying the series. More to come, for sure!
Comment by Abdo Saied Anwar on 2013-09-22 12:46:00 +0000
I don’t understand what these tow lines exactly do
context.translate(posX, posY);
rotation
context.translate(-posX, -posY);
Comment by David Murphy on 2014-05-29 16:32:00 +0000
I tried copying the code to jsfiddle and the HTML and CSS works, but no canvas. i.e., I put the HTML in the HTML box, CSS n the CSS box, and then javascript in the javascript box. No error messages, just did not work. Any ideas?
Comment by devhammer on 2014-05-29 16:42:00 +0000
Hi David,
Sorry you’re having trouble with it. I just checked the code here:
http://jsfiddle.net/devhammer/qf9VG/light/
And it’s working for me. Can you try that link? If it’s still not working for you, can you try opening the dev tools (in both IE and Chrome you can just hit F12), reload the page, and see if there are any errors in the console?
Comment by devhammer on 2014-05-29 16:45:00 +0000
Sorry I missed this comment somehow. Albeit a little late, the answer to your question is that because the Canvas rotation API rotates the drawing context (not the object you’re drawing), it’s necessary to translate the drawing context to the center of the object being drawn, do the rotation, then translate it back, in order to get the correct effect.
Hope that helps!
Comment by Guest on 2014-06-28 01:01:00 +0000
I am creating an array of objects and doing my animation using requestAnimationFrame. The problem is that the array of objects does not seem to be global. In javascript console, the error message is ‘Cannot read property ‘length’ of undefined’. (I guess the undefined being the array of objects). When I create the array inside the game loop, I can at least see the objects in my canvas.
Comment by devhammer on 2014-06-30 11:21:00 +0000
Hard to say without code what the issue is, but sounds like your array may not be in scope when you’re reading the length. I’d suggest looking at the code in my examples again, and see what’s different from what you’re doing. Good luck!
Comment by Ronan on 2015-08-02 13:03:58 +0000
Great tutorial series thanks!