Create canvas-based tweens with TweenJS

TweenJS can make the process of drawing on canvas and handling animation a whole lot easier


Drawing on canvas using just the API is tricky, requiring at least a working knowledge of the browser canvas API, as well as JavaScript itself. While the process can be made easier by using a library or framework, doing so can have its drawbacks. Sometimes the library that you haven chosen (often arbitrarily because you’ve previously heard of it, or a colleague recommended it) can provide just as many limitations to the task at hand as it offers solutions.

This tutorial is going to provide an introductory look at using TweenJS – a library designed to simplify the process of animating tweens on canvas elements. By focusing on this one potentially troublesome aspect of using <canvas>, and remaining compatible with the native canvas API, TweenJS offers a great lightweight solution to speeding up your workflow without imposing any real limitations.


A basic page

All of the vector graphics will be created using the EaselJS library, which abstracts the core canvas API to make it as easy as possible to get everything going. Not much is needed in the way of an HTML page to get started, so quickly create a blank document with a single <canvas> element. Give the <canvas> an id of mycanvas, add a width and height of 900px, then make sure to save the file as index.html.

 002 <html lang=”en”>
 003     <head>
 004         <title>TweenJS Example</title>
 005         <meta charset=”utf-8”>
 006        <style type=”text/css”>
 007            body {
 008                margin: 0px;
 009                background: #000;
 010            }
 011            /* Centre the <canvas> on the page */
 012            #mycanvas {
 013                width:        900px;
 014                height:    900px;
 015                position:    relative;
 016                margin:    auto;
 017                display:    block;
 018                border:    1px solid #333;
 019            }
 020        </style>
 021     </head>
 022     <body>
 023        <canvas id=”mycanvas” width=”900”     height=”900”></canvas>
 024     </body>
 025 </html>

Include TweenJS

Visit and download the production scripts of both EaselJS and TweenJS for inclusion on your page. Once you’ve grabbed them, add the libraries to the page by using <script> tags in the <head> section of the page. You’ll also need to include the EventDispatcher and Ease plug-ins. Create a blank JavaScript file and add a final <script> tag in your HTML to point to it. This will hold the canvas drawing code.

001 <!-- The main Easel Library -->
 002 <script src=”scripts/easel.js”></script>
 003 <!-- Handle events -->
 004 <script src=”scripts/eventdispatcher.    js”></    script>
 005 <!-- The main Tween Library -->
 006 <script src=”scripts/tween.js”></script>
 007 <!-- Easing functions -->
 008 <script src=”scripts/ease.js”></script>
 009 <!-- Our Script! -->
 010 <script src=”scripts/vector.js”></script>

Locate the canvas

Before being able to do any drawing, it is necessary to programmatically connect to the canvas on the page using JavaScript. This is done by first grabbing a reference to the <canvas> element using the getElementById method, then using this reference to create a drawing instance. Add the code shown to the ‘vector.js’ file, which will be the one you’ll be working in for the remainder of the tutorial.

001 // This function will be called when the     page     DOM is ready for manipulation
 002 function init(){
 004     // Set up the stage and draw an object     on     it
 006     // Create an EaselJS stage object
 007     mycanvas = document.            getElementById(“mycanvas”);
 008     stage = new createjs.Stage(mycanvas);
 010     // The stage is now ready to be drawn     upon
 011 }

Basic drawing with EaselJS

Now everything should be prepped and ready to do some basic drawing in the browser using the EaselJS library. With the created 900px square canvas, you can plot and draw lines, shapes and text anywhere you like within these confines. Keep in mind that the origin point (0,0) is in the top left corner of the canvas, rather than the bottom left as you might have expected.

Basic shapes

The EaselJS graphics library includes a series of methods for drawing shapes and lines. One of the best ways to understand how it works is to get stuck straight in, so create a simple square that you can later use as part of the animation routine. Add the following lines of code to draw a square now – you’ll be able to see exactly what’s going on in the next step.

 001     var square = new createjs.Shape();
 005     // a nice orange colour!
 008            drawRect(100,100,200,200);
 009     stage.addChild(square);
 011 <body onload=”init()”>

What’s happening?

You can get a full reference guide by looking in the documentation folder within the EaselJS folder, but briefly, a stroke (outline) style of 5px has been set, then a rectangle drawn starting at 100,100 and extending 200px on the X axis, and 200px on the Y axis. Then it is added to the canvas to make it display on screen.

Missing links

A square has been drawn in the previous steps, but at the moment there’s no way of remembering it; once the square is drawn, unless there is a reference kept to it, it’s forgotten. In order to be able to manipulate its position, you need to store a note of it. Change the code to look like that shown below, which remembers the square for easy later access.

001     var tweens;
 003     function init() {
 005        tweens = [];
 007        var square = new createjs.Shape();
 011        // a nice orange colour!
 014            drawRect(100,100,200,200);
 015        tweens.push({ref:square});
 016        stage.addChild(square);
 018    }

Lots of squares!

Now you know how to create a square and store a reference to it, use this knowledge to create 25 squares instead of just one. While you’re at it, position each one randomly on the canvas, and make each one slightly bigger than the last. Change the code to look like the following, then give it a try.

001     var tweens;
002     var squareCount = 25;
003     function init() {
005        tweens = [];
006        for (var i=0; i<squareCount; i++) {
007            // draw the square, and put it on     stage:
008            var square = new createjs.Shape();
009  ;
010              beginStroke(“#ff9900”);
011              drawRect(0,0,(i+1)*6,(i+1)*6);
012            square.alpha = 1-i*0.02;
013            square.x = Math.random()*550;
014            square.y = Math.random()*400;
015            tweens.push({tween:tween,         ref:square});
016            stage.addChild(square);
017        }
018     }

Set the colour

At the moment, the squares block each other out as they overlap. It’s easy to improve the way this looks by simply using a blending mode for the squares, allowing their colour to interact and making the entire canvas take on a bit of zing. Add the code shown below to the square creation loop.

001     var tweens;
 002     var squareCount = 25;
 003     function init() {
 004        tweens = [];
 005        for (var i=0; i<squareCount; i++) {
 006            // draw the square, and put it on     stage:
 007            var square = new createjs.Shape();
 008  ;
 009              beginStroke(“#ff9900”);
 010              drawRect(0,0,(i+1)*6,(i+1)*6);
 011            square.alpha = 1-i*0.02;
 012            square.x = Math.random()*550;
 013            square.y = Math.random()*400;
 014            square.compositeOperation = “lighter”;
 015            tweens.push({ref:square});
 016            stage.addChild(square);
 017        }
 018     }

Animate positions

It’s now possible to animate the squares using the TweenJS library. Start off by moving each square into the same position, so that you end up with a set of concentric squares.  You’ll animate to this position after the squares have been drawn in their random positions.

001     var tweens;
 002     var squareCount = 25;
 003     function init() {
 005        tweens = [];
 006        for (var i=0; i<squareCount; i++) {
 007            // draw the square, and put it on     stage:
 008            var square = new createjs.Shape();
 009  ;
 010              beginStroke(“#ff9900”);
 011              drawRect(0,0,(i+1)*6,(i+1)*6);
 012            square.alpha = 1-i*0.02;
 013            square.x = Math.random()*550;
 014            square.y = Math.random()*400;
 015            square.compositeOperation = “lighter”;
 016            var tween = createjs.Tween.        get(square).to({x:275-(i*3),y:200-(i*3)},     (0.5+i*0.04)*1500, createjs.Ease.bounceOut).    call(tweenComplete);
 017            tweens.push({tween:tween,         ref:square});
 018            stage.addChild(square);
 019        }
 020        createjs.Ticker.addEventListener(“tick”,     tick);
 021     }
 023     function tick(event) {
 024        stage.update(event);
 025     }

What’s going on?

The TweenJS library is automatically handling the movement of the squares from their starting position to their final positions on the stage. All we had to do was pass in the final position we’d like the squares to arrive at, and the easing method we’d like to use.

The tweens array

You’ll notice that the squares tween has been added to the tweens array at the end of the loop. This is an easy way to keep track of both the tween being applied, and the object it’s applied to. These references will be used a little later on to animate the squares again in response to a mouse click.

001 var tween = createjs.Tween.get(square).
 002 to({x:275-(i*3),y:200-(i*3)},         (0.5+i*0.04)*1500, createjs.Ease.bounceOut).    call(tweenComplete);
 003 tweens.push({tween:tween, ref:square});

Easy does it

There are many different easing methods available to make use of. Each offers a different way of mathematically calculating which position the object being animated should occupy at a specific point in time. Experiment with the different options (check the documentation for details), and find one you like. The code below uses bounceOut.

001 var tween = createjs.Tween.get(square).
 002 to({x:275-(i*3),y:200-(i*3)},         (0.5+i*0.04)*1500, createjs.Ease.bounceOut).    call(tweenComplete);

Click to animate

Now you can start writing a function that will fire each time the mouse button is pressed. Aim for an effect where the squares animate to the position where the mouse was clicked, allowing the user to change the position of the squares as they like. Start off by adding the basic function shown below.
001 function clicked(event) {
 002     // This function will be fired when the     mouse is clicked
 004     // Loop through each of the squares and     provide a tween
 005 }
 006 stage.addEventListener(“stagemouseup”,     clicked);

Make it happen

Now a function has been set up, you can work through each of the squares and set it to animate into position in the same way you did for the opening animation, but this time using the mouse-click position instead of the hard-coded values for X and Y.

001 function clicked(event) {
 002     for (var i=0; i<squaresCount; i++) {
 003        var ref = tweens[i].ref;
 004        var tween = tweens[i].tween;
 005        createjs.Tween.get(ref,{override:true}).    to({x:stage.mouseX,y:stage.mouseY},         (0.5+i*0.04)*1500, createjs.Ease.bounceOut).    call(tweenComplete);
 006     }
 007     activeCount = squaresCount;
 008 }

The ticker

You’ll find that there’s no need to worry about when to draw and update – it’s all handled automatically by the tick() function. The tick function has been added as the target for the tick event. This is an internal object that provides abstracted access to the requestAnimationFrame() method – see  the ‘Why use a ticker?’ section on the previous page for more details.

001 function tick(event) {
 002     stage.update(event);
 003 }

Test and perfect

There’s no need to continually redraw the canvas if nothing is being animated, so each time a square finishes its tween, just make a note – and if no squares are being animated, you can prevent the redraw from happening at all. Doing this will save processor power and battery life for your users. Once completed, give it a quick test and review!

001 function tweenComplete() {
 002     activeCount--;
 003 }
 005 function tick(event) {
 006     if (activeCount) { 
 007         stage.update(event); 
 008     }
 009 }