# Canvas 2D Web Apps/Animations

This chapter explains how to use keyframe animations with cui2d. Specifically, it shows how to change numeric variables according to a predefined array of keyframes. If the changing variables are used in the process function as coordinates, colors, etc., the resulting graphics will appear to be animated. Of course, other ways of implementing animations with the canvas element exist, but the presented approach has some advantages and is extremely flexible.

### The Example

The example of this chapter (which is available online; also as downloadable version) extends the example of the chapter on responsive buttons. Thus, the following sections only discuss the animation-specific parts of the code; see the chapter on responsive buttons and previous chapters for discussions of other parts.
In the example, three buttons are used to start three different animations. If clicked again, the first button just restarts the animation (even if it is already playing). The second button doesn't restart the animation if it is already playing. Lastly, the third button is inactive while its animation is playing and this state is also visually communicated by drawing it semitransparently, which achieves the effect of “graying out.”

### Defining Animations

The example defines three keyframe animations for animating a single image: widthAndHeightAnimation animates the width and height of the image to create a wobbling effect; yAnimation is used to create an animated jump by changing the y coordinate of the image; and xAndAngleAnimation is used to change the x coordinate and a rotation angle of the image to create a rocking motion:
Additionally, three keyframe arrays (one for each animation) are defined by these lines:
Each array is defined with the syntax [ 0th keyframe , 1st keyframe , ... ]. The first two arrays define the individual keyframes as objects with the properties time, in, out, and values. However, the in property of the 0th keyframe and the out property of the last keyframe is not required. time is the time in seconds of the keyframe after the start of the animation. Note that keyframes have to be specified in ascending order of their times. in and out define the velocities of change (i.e. the tangents or slopes) with which values are changing right before and right after each keyframe. The most important choices are:
* 0 for zero velocity, i.e. slow in/out (also known as ease in/out)
* 1 for the velocity defined by a smooth Catmull-Rom spline (but it is only smooth if in and out of one keyframe are the same)
* -1 for a constant velocity to the neighboring keyframe (also known as linear interpolation, but it will only be linear interpolation if the corresponding tangent of the neighboring keyframe is also specified by a -1)
For faster or slower velocities, these numbers can be scaled; i.e. a value of 0.5 specifies half the velocity that the Catmull-Rom spline would use. A value of -3 specifies three times the velocity that a linear interpolation would use. In between the keyframes, a cubic Hermite spline is applied. (Catmull-Rom splines and linear interpolation are both special cases of the cubic Hermite spline.)
The values property is an array of the actual data values at the keyframe. Their actual meaning (whether they are coordinates, color components, or sizes etc.) depends on how the animated values are actually used later on. In this example, yKeys specifies keyframes only for the y coordinate of an image; therefore, the values properties of the keyframes are arrays of just one number, e.g. [200] for an array with one coordinate of value 200 pixels. xAndAngleKeys animate an x coordinate and a rotation angle at the same time; therefore, the values properties are arrays of two elements. For example, [90, -20] specifies an array of two elements, where the first one will be interpreted as an x coordinate of value 90 pixels and the second as an rotation angle of value -20 degrees.
The third array of keyframes xAndAngleKeys uses the constructor cuiKeyframe(time, in, out, values) to construct objects with the properties time, in, out, and values. It is up to you, which way of initializing keyframes you prefer.

### Starting Animations

After animations have been defined as discussed in the previous section, they can be played (when processing events) with the function play(keyframes, stretch, isLooping).
This function sets the properties keyframes, stretch and isLooping with the specified arguments. In the example, the keyframes arguments are widthAndHeightKeys, xAndAngleKeys, and yKeys for the three animations. The second argument (which determines the stretch property) is a factor to make the animation longer than defined by the keyframes (with a factor greater than 1), or shorter (with a factor less than 1). This is useful in order to change the overall speed of an animation without changing the time coordinates of all keyframes. The third argument allows to play the animation in an endless loop. (Which can be stopped with the function stopLooping).
In the example, the first button just restarts the animation widthAndHeightAnimation whenever the button is clicked:
The second button checks whether the animation is not playing (with isPlaying) before restarting the animation:
Thus, the animation cannot be restarted while it is playing. In a sense, this makes the button inactive while the animation is playing. To communicate inactive buttons, many GUIs “gray out” these buttons. The third button achieves a similar effect by using semitransparent rendering and only drawing the image imageNormalButton:
cuiContext.save saves the current global alpha (which controls the opaqueness of the draw commands), cuiContext.globalAlpha = 0.2; sets the global alpha to a rather low value, i.e. the opaqueness is strongly reduced, i.e. the following images are rendered almost transparently. After the button is drawn with process, the global alpha is restored again with cuiContext.restore;. Note that process is only called if event is null, which just means that the button is inactive and doesn't react to events.

### Animating Values

The values of the keyframes of an animation are animated (i.e. interpolated for the current time) with the function animateValues. The result of animateValues is an array of values just like the ones that are specified in the array of keyframes but with the interpolated values for the current time. In the example, this is used this way:
Each call to animateValues returns an array of the current values of animated values. From these arrays, individual elements are extracted and assigned to certain variables (here: x, angle, y, width, and height). These variables are then used to draw the animated image. Note that by simply redrawing the canvas in every frame of the animation, complex animations are much easier to implement than if we had to individually modify all animated attributes of all animated objects.
The actual drawing of the animated image in the example is defined next:
The most important line (and the only line that actually draws something) is this:
It draws the image imageAlien centered at coordinates x and y at a size of width × height. (The coordinates x - width/2 and y - height/2 specify the top, left corner.) Since the values of x, y, width, and height are all varying with time, this covers most of the animation. The rest of the code is only required to handle the rotation by angle.
To this end, we apply the line cuiContext.rotate(angle * Math.PI / 180.0); which tells cuiContext to rotate all following drawings by angle. (The multiplication with Math.PI / 180.0 transforms a value in degrees to radians.) However, this rotation is always around the origin of the coordinate system. Thus, we have to translate (i.e. move) the pivot point of the rotation, which is at coordinates x and y in this example, to the origin of the coordinate system (i.e. to coordinates x=0 and y=0) in order to rotate around it. This is achieved with the line cuiContext.translate(-x, -y);. After the rotation, we have to move the pivot point back to its original position with cuiContext.translate(x, y);. If you look at the code, you'll see that these translations actually have to be specified in the reverse order. (If you want to read the transformations in the order they are specified, you have to think about how the coordinate system is transformed: cuiContext.translate(x, y); moves the origin of the coordinate system to our pivot point, then we rotate around the origin (at the new position of our pivot point), and then we move the origin back to its original position.)
Since we don't want this rotation to affect any further drawing commands, we have to restore the standard transformation again. The best way to do this, is to first save the current settings (i.e. state) of the context (which includes the transformation but also the fill color, font settings, etc.) with cuiContext.save; and once we are done with drawing, these settings can be restored with cuiContext.restore; .
This completes the discussion of how an application programmer would use the animation system of the example. The next section discusses, how it is implement in cui2d.js.

### Implementing the Animation System

First, the constructors simply set the properties of the cuiKeyframe and cuiAnimation objects:
As mentioned, the function play starts an animation and stopLooping stops the looping:
Basically, play just sets the properties start and end. Both are times in milliseconds since January 1st, 1970. The values are based on the current time (for start) and additionally the time property of the last keyframe scaled by stretch (and converted from seconds after the start of the animation to milliseconds after January 1, 1970). Furthermore, the global variable cuiAnimationsEnd might be set if necessary. cuiAnimationsEnd specifies the time (in milliseconds after January 1, 1970) when all animations are over. Thus, it only needs to be updated if the current animation is supposed to stop after all other animations. Lastly, cuiRepaint is called to request a redraw as soon as possible.
We also define a helper function isPlaying that returns true or false to specify whether a specific animation is currently playing (see above for examples how to use it):
In order to continuously repaint the canvas, the render loop is checking whether any animation is playing:
It checks whether any animation is playing by comparing cuiAnimationsEnd with the current time and updates the global variable cuiAnimationsArePlaying accordingly. Note that cuiRepaint is called when the animations just stopped. This is required because the canvas might change after an animation has stopped. If cuiAnimationsArePlaying is true, a new frame is rendered by calling cuiProcess(null).
Apart from the actual computation of the animated values (which is discussed next), this completes the description of the animation system. Note that the system allows for restarting animations that are already playing, it allows for any number of animations playing at the same time, it allows for the animation of an unlimited number of values with a single array of keyframes, and it allows to employ arbitrary cubic Hermite splines for the animation of values while making it easy to specify slow in/out motions (by setting the in and out properties of specific keyframes to 0), Catmull-Rom splines (by setting all in and out properties to 1) and linear interpolation (by setting them all to -1). The last feature is implemented by the interpolation of values, which is discussed next.
The values of the keyframes in an animation are animated (i.e. interpolated for the current time) by the function animateValues. As mentioned, the result of animateValues is an array of values just like the ones that are specified in the array of keyframes but with the interpolated values for the current time. The implementation of animateValues basically evaluates a cubic Hermite spline for every element of the values array with scaled slopes from Catmull-Rom splines or from linear interpolation (depending on the sign of the in and out properties). Unfortunately, the evaluation of splines is somewhat tedious and the mixing of Catmull-Rom splines with linear interpolation doesn't make it easier; thus, we are not going to discuss this code in detail.
However, there is an important feature at the start of the function: it first checks whether the animation hasn't started yet or whether the start and end time don't make sense (which is the case after they are both initialized to 0). In this case, the values array of the 0th keyframe is returned. Then it checks whether the animation is already over. In that case, the values array of the last keyframe is returned. This feature makes it possible to call animateValues even if the animation is not playing and this is actually exploited in the example of the three animations described above.
< Canvas 2D Web Apps