EaselJS Animation and Ticker
Synopsis: Create a simple programmatic animation, and learn about the Ticker class.
Topics: animation, Ticker, framerate, timingMode, getTime, paused,
requestAnimationFrame, Stage.update, time based animation
Target: EaselJS v0.6.0 and above
This tutorial is part of the EaselJS GitHub
repository.
Check out the repository for more tutorials and a handful of helpful samples.
Animation Basics
Animation is simply changing the visual properties of an object over time. There are a number of tweening classes that can make this easy (such as TweenJS and TweenLite), but in this tutorial we will explore the basic concepts without using one.
If you run the following code on a regular interval, the circle will animate moving to the right:
Simple, but what's the best way to set up that regular interval? You could use your own implementation
with setInterval, setTimeout, or requestAnimationFrame, and EaselJS would work perfectly, as long as you
remember to call stage.update()
after updating your display list.
To make things easy, EaselJS comes with the Ticker
class, which provides a regular
heartbeat (tick) for your application, provides pause and time deltas, and wraps both setTimeout and
requestAnimationFrame so you can use them interchangeably.
Ticker
The Ticker
class provides a simple static interface (meaning, you don't ever create a
new createjs.Ticker()
) to propagate a tick to various objects. To use it we just add an
event listener with addEventListener("tick", handler)
. The listener can be a function, or
an object with a handleEvent
function defined.
The code below adds the window as a listener, and defines a tick function that will be called 20 times
per second (Ticker
's default framerate):
You can easily change the default framerate by either setting an interval (the time between ticks) or a framerate (the number of ticks per second).
Let's combine all of that to make a circle move across the stage at 30 frames per second. And don't
forget to call stage.update()
at the end of each tick to draw the changes to the canvas!
Check out the source for simple.html for the complete code.
Time based animation
For many applications, it's a good idea to make your animations independent of your frame rate. This allows you to change the framerate dynamically, and ensures your animations run for the same amount of time, even if they are running on a slow device that isn't maintaining the target framerate.
Ticker
makes time based animations easy, by passing your listener a parameter that
indicates the amount of time that has elapsed since the previous tick. It also exposes a getTime
method which provides you with the total time elapsed since Ticker
initialized.
Now you can change the framerate, and the circle will take the same amount of time to cross the canvas (give or take a few milliseconds).
Notice in the above demo how when you change the FPS, the red circle still moves at the same velocity, whereas the blue circle's velocity is relative to the framerate. Also note how at 20fps, the red circle moves very slightly faster than the blue circle, because it accounts for frames that take slightly longer than the expected 50ms.
It's also worth noting that sprite sheet animations in EaselJS also support time-based playback via the
Sprite.framerate
and SpriteSheet.framerate
properties. See the API docs for
more information.
Note: The framerate
property of Sprite and SpriteSheet will only work if the stage.update()
method is passed the event
object that is generated by the Ticker:
Timing Modes
Most modern browsers support a new animation related API called requestAnimationFrame
. It
provides the benefit of syncing programmatic changes with screen redraws which can result in smoother
looking content. It will also throttle the framerate of background content (such as an unfocused tab) to
reduce CPU and battery use.
The Ticker.timingMode
allows you to choose what API to use to drive the heartbeat. By
default, it uses Ticker.TIMEOUT
and the setTimeout API. This is supported in all browsers,
and provides a predictable, elastic framerate but without the benefits of requestAnimationFrame. It also
allows you to reduce CPU/GPU load by reducing framerate.
There are two modes for using requestAnimationFrame, which will automatically fall back to using
setTimeout if the browser does not support the API. Ticker.RAF
simply passes through the
RAF heartbeat, ignoring the specified framerate completely. Because RAF frequency is indeterminate, your
content should be time based when using this mode.
The Ticker.RAF_SYNCHED
mode attempts to match up the requestAnimationFrame heartbeat to
your specified framerate. This mode provides many of the benefits of the TIMEOUT
and
RAF
modes, but will generally have the highest variance in frame times. It typically works
best when the framerate is set to a divisor of 60 (ex. 10, 12, 15, 20, 30).
Pausing
Ticker
also provides the ability to pause all of your animations. All listener you add to
Ticker are "pauseable". Calling Ticker.paused = true;
will stop Ticker
from
calling tick
on all pauseable listeners.
The getTime
method accepts a pauseable
parameter, and will return the
appropriate time total based on it.
In the following demo, play with toggling pause, and see how the red "pauseable" circle stops, where the
green "unpauseable" circle does not. You can also see how the total pauseable time stops updating when
Ticker
is paused.
Tick
When you call stage.update()
, it will tick any descendants exposing a tick method and
render its entire display list to the canvas. Any parameters passed to update()
will be
passed on to tick()
handlers. This lets you handle your animation in the context of your
display object.
You can also add your stage directly as a listener to Ticker
, because Stage
has a handleEvent
method that shortcuts to update
. It's rare that you would
want to use this in a larger application, but it can be handy for quick tests.
Performance
Remember that a higher framerate doesn't always result in smoother animation. A lower framerate uses less CPU, and can provide more consistent performance. Try to find the right balance for your project.
If you'd like to check what your real framerate is, you can call Ticker.getMeasuredFPS()
to
get the average real framerate for the past 1 second. You can also use Ticker.getMeasuredTickTime()
to get the average time spent within the entire tick event stack.
TweenJS
If you're planning to do a lot of animation, you might want to check out TweenJS or another tweening library such as GSAP. You can use simple commands to tween properties over time and create sequences of animations.