Animation Tutorial

In this tutorial you will learn several different techniques for animating Bindows components.

Animation Classes

There are four classes you need to be aware of when animating. First there is the base class called BiComponentAnimation. Unless you are building your own animation classes you will, most likely, only use a subset of its methods, such as start, stop and addComponent.
When a component is animated, its properties are periodically changed over time. Currently there are three properties that can be animated; opacity, location and size. Each has its own animator class. So the three animator classes are

By combining the effects from all three classes, you can create complex synchronized compound animations.

Key Classes Overview

Animation Basics

The procedure of animation is comprised by the following steps:

  1. Create an animator object,
  2. Add Bindows components to the animator
  3. Start the animation
  4. Dispose the animator object when it's no longer needed

Because the animators operate on BiComponents, you can animate virtually everything in Bindows, like calendars, labels, windows, buttons and images etc.
All animators also dispatch events that you can listen to, such as animationend and frameprogression. This can be useful if you need to know when an animation has finished playing etc.

Example 1 - Markup Animation

The following example illustrates how to iconize an image across the screen, using xml only. When the animation has finished playing, the image is made to pulsate. Run the example and take a look at the code. Try to figure out what's going on and then follow our discussion on this example.

View AnimationTest2 source

Discussion 1

The Image tag sets up an image in the top left corner of the application window. The image is then associated with the three animators declared below. The animation property is a comma-separated list of animator ids. An id must be denoted by a # sign. The association will cause the animators to operate on the image and animate it.

The LocationAnimator is given an id and is then set to autostart. This causes the animation to start as soon as the application is fully loaded. The animator is then instructed to move objects from one point to another on the screen. It is a good idea to make the fromX/Y the same as the image's current position on the screen; otherwise it will jump to fromX/Y when the animation starts. The type is set to smooth. This is a feature that governs the acceleration and deceleration of the animation. You can find several different types available in the API documentation. The speed is set to 1000. This means that the animation will be 1000 milliseconds long.

The SizeAnimator has got a similar setup but is now using from/to width/height instead. Note that the destination size is set with the toSize property. This is a shortcut that sets both width and height to the same value. So it will be square sized. Speed is set a little differently here. There are five speed constants that can be set using the string values; slowest, slow, normal, fast and fastest. Fast happens to be 1000 ms so the location and size animation take the same amount of time to complete, hence they will be synchronized.

The OpacityAnimator looks simple enough but its autostart property is set to false. We want to start this later when the other two animations have finished playing.

At last, in the code, we add an event listener, to the location animator, listening for the animationend event. This is dispatched when an animation has reached an end point. In the handler for the event we tell the opacity animator to start animating. The logo will start to pulsate.

Example 2 - Motion Paths

In this example we explore how to set up motion paths for the location animator. A motion path is simply two points on the screen that you want your component to move between. Take a look at the sample and continue reading in the discussion.

View AnimationTest3 source

Discussion 2

Here an image has been added to position 0,0 on the screen using the xml. The location animator is created in code going from coordinate 0,0 to 400,400. The duration of the animation is set to 1 second. It is currently not looping, but we later override this with the setLooping method. The next parameter is the acceleration behaviour type. You find different constants for different types in BiComponentAnimation. By setting the frame rate to 80 we are guaranteed a fairly smooth animation experience. If you set this to null, Bindows will choose the frame rate for you. The last parameter, autostart, is not given, so it will default to false. All parameters to the constructors of all animators are optional and can be set later.

The image component is then registered with the location animator using addComponent. Theoretically, multiple components could be added to the location animator, but this is a more interesting feature for size and opacity animation because with a location animator all components would have been animated from and to the same coordinates.

The pushPath command adds points that the component will travel between. These are called animation paths. Each animation path can have its own speed/duration and acceleration curve. Animation paths are stored in a stack and pushPath adds animation paths at the end of the stack. It is a good idea to set the start of the next path to the same value as the end of the previous one, unless you want your component to jump.

Looping is then activated and the animation is started.

Example 3 - Using Convenience Methods

Sometimes you just want to animate something really quickly using a standard visual effect. Maybe you want something to blink a couple of times, to get the users' attention. Or maybe you just want to slide a window to a new position. This is a good opportunity to use the static convenience functions. In the following example we show this and also how to dispose your animators correctly.

View AnimationTest4 source

Discussion 3

There are essentially two types of static convenience functions inside the animator classes. Those that return a new animator object and those that do not. The type that do not return anything starts automatically and disposes themselves after the animation has finished playing. You can't modify their behaviour after creation. They are essentially, one-off animations. The other kind, that do return a new animator object, offer a more simple interface then the animator's constructor. You can reuse and change this as you like, but you must also dispose it.

Disposing can be done on animationend or anywhere else in you program. Another thing about the static convenience functions is that they take a BiComponent as a parameter in their function call. This is the component that will be animated and it saves you from using addComponent, but only one can be added this way.

Example 4 - Relative Positioning

So far we have only used absolute positioning and the size of the application window has been locked. But what can you do if you have a Bindows application that can be dynamically resized? What if your animation depends on components that are moved and resized by a layout manager? You could off course hookup events and change the animations when needed but Bindows can do all this for you. Now we will take a look at how to parent your animations to other Bindows components.

The location animator have the ability to set top, left, right, bottom for both the start state and the end state. E.g. you want component X to animate from component A to component B, regardless of where A and B are on the screen. If A or B are somehow moved, the animator should update itself. The following example illustrates this. Click with the mouse on different places on the screen to reposition the one of the components.

View AnimationTest6 source

Discussion 4

In this example; logo1 is animated in a loop between logo0 and logo2. By using setFromComponent, the location animator will fetch its start x/y position from logo0 and its end x/y from logo2. You could say that the animation inherits its location properties from logo0 and logo2. If logo0 and logo2 are moved or resized, the animation will know about it and update itself accordingly. You can animate to/from any corner of the to/from component. By default when you specify a to/from component, the animator takes that component's top left corner. You can order it to use the bottom or right with the setFromBottom or setToRight methods for example. These methods also takes offsets, so you can position 10 pixels away from the bottom etc.

Note that the location animator can only affect location, it cannot resize components, so specifying both top and bottom does not cause the component to resize. If you first specify top and then bottom, then top will be cancelled and bottom will be used instead. You don't have to specify to/from components. If you don't; top, left, right and bottom will apply to the application window's coordinates.

Example 5 - Relative Resize

The size animator is also capable of relative animation with dynamic updates. When dealing with size only, it doesn't make sense to talk about things like top or bottom etc. so that terminology is not used with the size animator. But you can set a to/from component. The size animator will copy the width and length of those components. It can also sense when any of these are resized and update itself.

In the following example the middle component's size is animated. It's starting size is taken from the left component and its ending size is taken from the right component. The slider changes the size of the two components. Notice how the animation is adapting itself.

View AnimationTest7 source

Discussion 5

The starting/from size of the animation is set by setFromComponent and the ending/to size is governed by the component passed to setToComponent. If you don't specify a to/from component, it does not default to the screen. Instead it relies on absolute sizes passed to the constructor or the setFromHeight, setToSize etc.

Considerations and Problem Solving

You have now seen examples of synchronized animations but there are some limits and pitfalls that you should be aware of. Animations may not achieve synchronization unless you take the appropriate steps. To get a good sync, you should create and setup the animators first, then start them all at the same time. As soon as an animation is started it will start playing and will run its first frame before the second one gets to start. To achieve synchronization, those animations should be running on the same BiFPSGenerator. This ensures that, on each frame, both animations will be progressed accordingly. For this reason, the static convenience methods are not suitable for synchronized behaviour. If animations are running on different generators, you will be at the mercy of the browser's timeout methods, which can be unpredictable.

There are several ways at which you can make sure animations are running at the same generator. When you create your animator object you specify the frame rate as null or BiFPSGenerator.DEFAULT_FRAME_RATE and the animator will be connected to the global generator. If you set the frame rate to anything else you will get a unique generator. Another way to set a shared generator is to create two new animations, get the generator of the first and set it as the generator on the second.

The global generator saves resources and helps with synchronization amongst animations. But if you change its frame rate you will change the frame rate of all animations that use it. This could possibly be confusing but it could also be exactly what you want.

Setting a too high a frame rate can get you into problems. Especially when you are animating complex objects; like moving and resizing a calendar. You should be aware of the fact that, you will not achieve the frame rate that you set, because the frame rate is actually just a delay between frames. Also, every frame will render, there is no frame skip feature built in at the moment. If each frame takes a long time to render and you use many frames, your animation will take much longer to play out, effectively slowing it down. When this happens, you should lower the frame rate of that animation. It's a good idea to set it a little lower than your machine can handle.

Animating large components is also more CPU intensive than animating small ones, as the browser will have to recalculate larger portions of the screen. Animating components over complex backgrounds, such as layers of multiple components with different opacity, will also demand more resources.