Chapter 12. Animation

In the previous chapter, we talked about the Request class, which is the old new thing in the recent JavaScript revolution. Now we'll talk about an "old old thing:" animation.

In this chapter, we'll learn about the basic theory of animation and how objects are actually animated from one state to another. We'll then explore the elements that come together to create these animations in JavaScript, before finally diving into the MooTools Fx classes, the pinnacle of JavaScript animation today.

Getting Animated

When we hear the word animation, what generally comes to mind is motion—the movement of an object with regard to its original horizontal or vertical position. Animation tasks such as moving a square from left to right or bouncing a ball up and down are good examples. When animating the square, we might move it from its original position to another position to the right of the original, thus relocating the object horizontally. The bouncing ball animation, on the other hand, requires us to shift the ball vertically, first moving it up from its original position, then moving it back from the new position to the original.

These two examples give us an idea of some of the basic elements of animation. First we have the object to be animated, called the subject. In our examples, the subjects are the square and the ball. Then we have the particular property of the object that we need to change, which in our examples is the vertical or horizontal position of the square and ball subjects. And, finally, we have the value of that property itself.

Motion occurs when the original value of the property—which we designate as the initial value—changes. So when our square, which was originally 10 pixels to the left of the screen, moves 20 pixels to the right, we have apparent motion.

This changing of the initial value happens over time, which is another element of animation. When we start, our square will be 10 pixels to the left of the screen; then, after a second, it moves to a new position 20 pixels to the right of the initial position. The time it takes for the initial value to change to the new value is the duration of the animation.

Each change of the initial value is called a frame. In our square example, for instance, we might have two frames: our first frame would show the square 10 pixels from the left of the screen, while the second frame would show the square 30 pixels from the left. Our first frame therefore represents the initial value of the property, while the second frame represents the change of the initial value equal to 20 pixels to the right of the initial value.

If our animation's duration is 1 second, we'll see our first frame, followed by the second frame after a second. We'll see the motion, of course, but it's a very jumpy sort of motion. It will seem that our square suddenly jumped 20 pixels to the right without any smooth movement in between. This happens because our eyes detect the abruptness of the movement: the change is so big that our eyes notice it immediately.

In order to make the animation smoother, we must be able to trick our eyes so they don't notice the abruptness of the movement. We can do this by making the change in the properties value smaller. In our example, this means that instead of simply changing the value of the square's position from 10 pixels left to 30 pixels, we must slowly increase the original 10 pixel value over time until it reaches 30 pixels from the left of the screen.

The way to do this is to add more frames. We could add, for example, another frame that will go in between the first and last frames. The first frame will show the square at 10 pixels to the left, the second will show it at 20 pixels and the last at 30 pixels. We then divide the duration—which is one second—accordingly, which means that frame 1 will be shown first, frame 2 will be shown half a second later, and frame 3 is shown after a full second. When done like this, the animation still won't look smooth, but it won't look so jumpy either. Because the amount of change was spread through the duration of the animation, our eyes won't complain as much.

But in order to make the animation really smooth, the number of frames should be sufficient that the change in each frame is small enough to go undetected by our eyes. The number of frames to use in an animation is called the frame rate and is measured in frames per second, or fps. The frame rate of an animation is simply the number of frames to be displayed each second of the animation's duration. If you have a two-second animation at a 24fps frame rate, for example, it means that 48 frames will be displayed during the span of two seconds. In our first frame example, we had a 1fps frame rate, while our second example had a 2fps frame rate.

Note

The first frame, which shows the initial state of the subject prior to any changes, isn't included in the framerate.

The frame rate of an animation is also used to determine the amount of change per frame. We do this by dividing the amount of change we want by the number of frames we'll have. In our square example, we want to change the value of the square's horizontal position from 10 pixels from the left to 30 pixels from the left, which means that the amount of change is 20 pixels. If our animation runs for 1 second at the frame rate of 1fps, then the amount of change per frame will be 20 pixels. On the other hand, if our animation runs for 1 second at the frame rate of 24fps, then the amount of change per frame will be around 0.8 pixels, which means that the horizontal position of our square will change 0.8 pixels for each frame until it reaches the final frame.

At this point we can formulate a good definition: animation is the process of gradually changing the value of a property over a specific period of time. Take note that while we've discussed animation here using the concept of motion (the change in the value of the horizontal or vertical position property), animation isn't limited to motion, especially when we're animating in JavaScript. For example, if we change an element's height or width through animation, the object remains in the same position—yet this is still considered animation. This is also true for animating things such as opacity or color, which have nothing to do with motion.

So now that we have a basic understanding of the concepts involved in animation, we can turn our attention to the implementation details. But before we actually animate anything in JavaScript, we must first understand how the animation concepts of property and time apply.

Being Stylish

The most common properties that are animated in JavaScript are the visual properties of elements, such as positions, dimensions, and colors. And these visual properties are controlled by CSS styles.

CSS Styles

At a global level, CSS styles are added to the page using style sheets, which contain style declarations:

div.post {
    color: #CCCCC;
    display: inline-block;
    height: 20px;
}

This is an example style declaration, which should be familiar to you by now, since we saw something like this in Chapter 9, on Slick. At the start of the declaration is the selector, which defines which elements to apply the style to, followed by the style rules that are surrounded by the curly braces.

Each of these style rules defines the value of a particular visual property of the elements to which the rules apply. This declaration, for example, tells us that all div elements with the class post will have a font color of light gray (color: #CCCCCC), a visual display that makes it both an inline and a block element (display: inline-block), and a vertical dimension of 20 pixels (height: 20px).

A style rule—and therefore a visual property of an element—is usually one of four types:

  • Numeric rules are rules whose possible values are numbers, such as font-size, line-height, or opacity.

  • Color rules are rules whose possible values are colors defined using either hexadecimal notation, such as #CCCCCC or #09FA0C, or a color function, such as rgb(255,0,51) or hsl(120,100%,75%).

  • String rules are rules whose possible values are strings (i.e., a sequence of characters), such as font-family or display.

  • Mixed rules are rules whose possible values can be a number, a color, a string, or combinations of these. Examples are shortcut rules like background and font, or rules like height or margin, which can accept both numbers and strings. Strictly speaking, all rules are mixed rules, since you can assign string values to them, such as auto and inherit.

All these rule types can be animated in one way or another, and we'll see how each of them is handled later on. An important thing to know right now, though, is that some rules—particularly color and numeric rules—are easier to animate than others.

We use style sheets to define these rules on a global level: style declarations in a style sheet are applied to all elements on a page that satisfy the selectors. But specific elements can also have their own set of styles that are defined using inline styles, which are style rules that are defined only for a specific element.

An inline style can be defined in HTML using the style attribute:

<div class="post" style="color: #000; display: inline;"></div>

Here we have a div element with a style attribute that defines its inline style. For this element, the color rule is given the #000 value, while the display rule is given the inline string value.

If this element appeared in the same document with a style sheet that includes the style declaration we have above, what would be the color and display visual property values of the div be? Would they be #CCCCCC and inline-block respectively, or #000 and inline? The answer, of course, is the latter. Inline styles take precedence over global styles, which means that if an element has an inline style rule declared, it will override any global styles for the same rule—except global rules declared with !important.

This precedence of inline styles over global styles is important because animation happens using inline rather than global styles. As we'll see later, it is the value of these inline styles that change rather than the global styles. This is because animation needs to be localized: if we change the value of an inline rule, it affects only the element where the inline rule is declared.

Explicit, Implicit, and Computed

The global styles and inline styles that we define for our documents are called explicit styles, because we explicitly define them using style rules in style sheets or inline styles. We can find out the explicit styles in our documents in two ways.

For global style rules, we can inspect the document.styleSheets object, which is a node collection containing all the style sheet objects in a page. Inspecting this object can sometimes be complicated, though, because it uses multiple interfaces and objects.

Inline styles, on the other hand, are available directly by accessing the style object property of an element. For our div example, for instance, we can get the value of its color style by accessing div.style.color, which should give us the hex string '#000'. For CSS rule names that are hyphenated, such as background-color, the style object will use a camelcase variation of the name, like backgroundColor. If an element doesn't have an inline style defined, the value of the property will be an empty string, which means that accessing div.style.background in our example will yield "".

Explicit styles are only one piece of the puzzle, though. All style rules have, in fact, a default value. For example, the default value of the height rule is auto, which means that the item's height depends on its content. When creating style sheets or inline styles, we usually don't write down the values for all rules, but only for the rules whose default values are different from the ones we want. Styles whose values are not defined directly in the style sheet are said to be implicit styles. Included in the implicit styles category are styles that are inherited by an element from an ancestor or parent, such as color.

When it comes to styles, explicit styles trump implicit styles. Inline explicit styles are honored more than global explicit styles, and both inline and global explicit styles come before implicit styles. When you combine both the explicit and implicit styles that apply to an element, you get the complete set of styles for an element, and thus you get the element's computed styles.

Computed styles represent the final style that's applied to an element. For our div snippet, for example, the computed style for the color of the div will be #000, which is equal to the definition in the inline style, while the computed style for the height of the div will be 20px, which is equal to the definition in the global style sheet. And since there was no definition for it, the computed background-color style of the div will be transparent—the default implicit value for the style.

When dealing with computed styles, it's important to note that values such as auto don't exist because computed styles have concrete values. An auto value usually represents a value that automatically changes depending on the state of an element, like height based on the content of the element. When computed styles come into play, these auto values are turned into actual pixel values—so an element with a height set to auto in a declaration will have a height value equal to X pixels in computed styles.

This concreteness of computed styles is important because we need to deal with real values in animation. If we want to animate the height of a div by adding 20 pixels to it, for example, we need to be able to get the initial pixel value of the element's height so that we can increment it per frame. We can't do this using ambiguous values like auto—we need the actual, concrete numeric value.

Revisiting Style Methods

In Chapter 8, when we talked about the Element type, we learned about the basic style methods such as getStyle and setStyle. We said that these methods are used to return and set the specific styles of the elements, and in part that's true. But since we now know more about CSS styles in general, we need to expand our discussion a bit.

The getStyle and setStyle methods can be considered "unified" methods: both can be used as interfaces to other related functions. The setStyle method, for instance, is mainly a wrapper for assigning properties to an element's style object. This means that doing something like el.setStyle('height', 'auto') is the same as doing el.style.height = 'auto'. Well, almost the same: aside from this simple style object assignment, setStyle can also handle things like setting opacity, which is really done using set('opacity'). Thus, we go back to the original premise: setStyle is a single interface to several different operations.

Like setStyle, getStyle is largely concerned with the style object. When we try to access an element's style value using getStyle, the method first checks the style object of the element to see if the specific rule is present. If it is, the value of this rule is simply parsed into a usable form and returned. However, if this rule isn't present in the style object, getStyle will call another method: getComputedStyle.

The getComputedStyle method has the same API as getStyle, but it checks the computed style of an element directly rather than checking the style object first. When we retrieve an element's style rule value using getComputedStyle, we actually access the final style value that's applied to the element. This means that all rules—both explicit and implicit—are available using getComputedStyle, which makes it very valuable.

One typical question about getStyle and getComputedStyle goes like this: "If getComputedStyle can give us the final value for any element style rule whatsoever, then why doesn't getStyle simply use getComputedStyle for everything?" This is a valid question. The truth is that for most cases, getStyle does use getComputedStyle for everything, since most elements won't have inline styles, which means they'll have blank style object properties. But on the other hand, MooTools does check the style object as much as it can for two reasons: because it's faster and because the value of the rule in the style object is actually the same as the value of the element's computed style, since inline styles have the highest precedence order.

Time for Some Action

Now that we've seen how properties come into play, let's take a look at the other piece of the animation puzzle: timers.

Timers

The existence of a main loop lets browsers provide special functions called timers. A timer is a function that enables us to execute a function at a particular time. There are two timer functions in JavaScript: setTimeout and setInterval.

The setTimeout function takes two arguments: func, which is the function to run, and delay, which is a number representing the amount of time to wait before executing the function.

setTimeout(function(){
    console.log('Hello World!'),
}, 1000);

Here's an example of setTimeout that executes a function after a second. You'll notice that the delay number is in milliseconds, where one thousand milliseconds is equal to a second. When this snippet is run in the browser, the JavaScript interpreter will wait one second before invoking the function.

The other timer function, setInterval, also takes two arguments: func, which is the function to run, and interval, which is a number representing the amount of time to wait before executing the function. This function works similarly to setTimeout, but instead of simply delaying the execution of the function, it invokes the function at regular intervals.

setInterval(function(){
    console.log('Hello World!'),
}, 1000);

In this snippet we use the same arguments as we did for the setTimeout function. And as with setTimeout, the JavaScript interpreter will again wait one second before invoking the function. Unlike setTimeout, though, which invokes the function only once, setInterval will continue invoking the function regularly for an indefinite period of time. So after the first invocation, the interpreter will wait another second before invoking the function again, and then repeat this wait-invoke process indefinitely.

Note

The setTimeout and setInterval functions aren't actually limited to functions, but can also be passed strings representing code to be evaluated using the eval function. This form is discouraged, however, because of the limited scope of the eval function and also because it can lead to some security issues regarding unescaped user input.

One important thing to know about these functions is that they're scheduling functions, not blocking ones. What this means is that when the interpreter sees an invocation of setTimeout or setInterval, it does not stop further execution in order to wait for these functions to finish. Instead, it takes the function arguments and the delay or interval value and stores them in a stack in order to access them later before continuing the execution. For example:

console.log('A'),
setTimeout(function(){
    console.log('B'),
}, 1000);
console.log('C'),

In this example we have a setTimeout call surrounded by two console.log invocations. When we run this on the browser, we get the following console output:

A
C
B

You'll notice that the order of the log is different from the order of console.log invocations in the snippet. This is because the interpreter doesn't wait for the function argument to be executed, but instead stores that function first. So what happened here is that the first log output, A, was logged first, then the interpreter saw the call to setTimeout. It then stored the function argument to setTimeout in a stack before proceeding to the next line to output C. When the delay period specified for setTimeout passed, the interpreter finally called the function argument, so the B value is logged.

Both setTimeout and setInterval return a number value, called a timer identifier. This number is used if we need to disable the timer, which is especially useful for setInterval. Disabling a timer is called "clearing," and can be done using two functions: clearTimeout and clearInterval.

The clearTimeout function takes a single argument, id, and disables a setTimeout timer associated with that id:

var id = setTimeout(function(){
    console.log('Hello World!'),
}, 1000);

clearTimeout(id);

In this snippet, we first stored the identifier of the timer in a variable called id. We then immediately called on clearTimeout, passing the identifier we stored. This means that the function will never be called, because we disable the execution of the timer function immediately.

As you might guess, clearInterval is used to disable a setInterval timer. Like clearTimeout, it also takes a single id argument:

var count = 0;

var id = setInterval(function(){
    console.log('Hello'),
    count++;
    if (count === 3) clearInterval(id);
}, 1000);

Here we have an example of a self-disabling interval function. We first set up a counter that increments for each invocation of the function argument. We then set the function argument to run every one second using the setInterval function. When the function argument has been invoked three times, we call clearInterval to disable the timer, stopping our function from further executing.

Since the setTimeout and setInterval functions are mostly concerned with executing function arguments, MooTools implements two Function methods that employ these two timer functions, delay and periodical, which we've already seen in Chapter 2. Like setTimeout and setInterval, these two function methods return a timer identifier that can be used with the clearTimeout and clearInterval functions.

Timer Execution

JavaScript's single-threaded nature has a very interesting effect on timers. Remember that the global scope and function scopes have separate execution contexts, which the interpreter "enters" and "exits" during the course of a program. Because JavaScript is single-threaded, only a single execution context can be active at any point in the program's lifetime.

This one-context-at-a-time design affects timers immensely because timers are run in a separate execution context. For instance, take a look at the following example:

var fn1 = function(){
    setTimeout(function fn2(){
        console.log('fn2'),
    }, 0);
    console.log('fn1'),
};

fn1();

In this snippet we created a new function called fn1. Inside this function, we created a new delayed function called fn2 using setTimeout. What's different about this delayed function is that we set the delay amount to 0—which is like saying we don't want any delay in execution. So, we would likely assume that since there is no delay, fn2 will be executed immediately and our output will be 'fn2' and then 'fn1'. But when we run this, we see the following output:

fn1
fn2

Even though our fn2 function had a delay of 0, it wasn't executed immediately. Instead, the interpreter finished executing fn1 first before invoking fn2.

This is an important point to consider when working with timers. Because JavaScript is single-threaded, only a single execution context can be active at a time. In our example, fn2 can't be executed immediately even if the delay is 0 because at the point the timer went off, the interpreter was still busy interpreting fn1.

But timers aren't only competing with regular functions for execution, they are also competing with other timers. If a function delayed using a timer is executing, other timers that are set off during that time won't be able to execute as well, which means they'll be queued for later execution.

It is imperative that you remember this because it shows us a "flaw" in the timer system: most of the time, we can't trust timers to execute at exactly the time we specified. If there are no functions currently being executed, our timers will probably execute with a delay that's equal to what we specified. But if our application is busy with other things, we can't expect the timer to be really on time.

A Basic JavaScript Animation

At this point we have all the elements we need to get started working with JavaScript animation. We know the basics of animation theory, we know about styles, and we know about timers. So let's go ahead and try implementing a simple animation with JavaScript.

For our purposes, we'll reuse the square example: a square that is 10 pixels from the left of the screen needs to be moved 20 pixels to the right until it reaches a position of 30 pixels from the left of the screen. For this attempt, we'll make everything really simple so that we can focus on the animation itself.

First, here's our element:

<div id="square" style="
    background: #000;
    height: 20px;
    width: 20px;
    position: absolute;
    left: 10px">
</div>

The element we're animating is a basic div element. To keep things simple, we've defined the dimensions and position of the element using an inline style. This makes it easier for us to get these dimensions in our code using the style property.

Now that we have our element, it's time to start writing our animation code. The first thing we need is to declare a few variables:

// the element
var square = $('square'),

// time/frames variables
var duration = 1000;
var rate = 50;
var frames = duration / 1000 * rate;

// property variables
var amount = 20;
var increment = amount / frames;

The first variable, square, is a reference to the element we're animating. The next three variables are our time and frames variables: duration tells us the length of the animation in milliseconds, rate tells us the frame rate or number of frames per second, and frames tells us the total number of frames for the whole animation sequence. Finally, we have the property values: amount, which tells us the total amount of change for the element's property, and increment, which is the amount to change per frame.

Now we need to create the step function. This function will be the one called to update the value of the div's property. There are many ways to do this, but we can simply code it like this:

var step = function(){
    // get the current value
    var left = parseFloat(square.style.left);

    // increment the value
    left += increment;

    // set the new value
    square.style.left = left + 'px';
};

The first thing the step function does is to get the current value of the property being animated, which it stores in the left variable. It then increments this original value with the amount we calculated by dividing the total change amount by the number of frames for the animation. Finally, the function sets this new value as the value of the left property of the element—thereby visibly moving the object on the screen.

We're almost done with this animation example. The last thing we need to do is to add the timer. Since we'll need to call the step function at regular intervals, we'll use the setInterval function:

var stepInterval = setInterval(step, Math.round(duration / frames));

Here we used setInterval to get the interpreter to run our step function at a regular rate. The speed of our interval function is defined as Math.round(duration / frames), which gives us a nice whole number to work with, since timer functions aren't that good with floating points.

Now we have everything in place. Let's take a look at our complete code:

// the element
var square = $('square'),

// time/frames variables
var duration = 1000;
var rate = 50;
var frames = duration / 1000 * rate;

// property variables
var amount = 20;
var increment = amount / frames;

var step = function(){
    // get the current value
    var left = parseFloat(square.style.left);

    // increment the value
    left += increment;

    // set the new value
    square.style.left = left + 'px';
};

var stepInterval = setInterval(step, Math.round(duration / frames));

The first time the step function is called by the interpreter's timer, the square will be moved a little bit to the right, and will continue moving for each subsequent invocation. The result is a nice animation sequence.

There's a problem with our implementation though: we didn't add anything that will make it stop! If we run this on the browser, the square will continue moving right indefinitely. Remember that we only want to move the square a few pixels to the right—20 pixels to be exact—but our previous example moves the square way past that amount.

// the element
var square = $('square'),

// time/frames variables
var duration = 1000;
var rate = 50;
var frames = duration / 1000 * rate;

// property variables
var initial = parseFloat(square.style.left);
var amount = 20;
var increment = amount / frames;

var step = function(){
    // get the current value
    var left = parseFloat(square.style.left);

    if (left < (initial + amount)){
        // increment the value
        left += increment;

        // set the new value
        square.style.left = left + 'px';
    } else {
        // stop the timer
        clearInterval(stepInterval);
    }
};

var stepInterval = setInterval(step, Math.round(duration / frames));

Here we added a new variable called initial, which stores the initial value of the property we're animating. We also modified our step function to check the value of the element's left property each time it's called. If the value is less than the sum of the initial value and the total change amount, then our animation isn't done yet, so we can continue incrementing the value. If it's not smaller, however, it means that the target amount of change has been satisfied, so we can stop the animation by clearing the timer.

Our example is pretty easy to understand, and the whole implementation is straightforward enough. As it shows, basic animation isn't that complicated: you only need to declare some basic variables with simple mathematical formulas, and employ a single function that will be repeated using a timer. Even with this simple code, the result is still quite exciting.

MooTools Fx Classes

While our animation example is great for showing the elements of animation in action, it has flaws that make it unsuitable for real-world use.

The first problem is the hard-coded property change. We're animating a single property in our example, but what if we need to add more properties to animate? Or what if we need to change this property? All the property-related elements in our example are hard-coded as variables, and the change process itself defines our property explicitly.

Another issue is reusability. What if we want to animate other things on our page? Do we copy the same function and just change it? That would be wasteful, not to mention impractical. In order to make animations really practical, we need a reusable way of adding animations to our page.

If we want to fully take advantage of animation in our applications, we need a better solution. Animation was one of the most common uses of JavaScript in the early days of the language—the other being validation—and even today, animation plays a big role in browser-based JavaScript applications, not only for the purpose of "eye-candy" but also to provide visual clues about UI changes to users.

The usefulness of animation was apparent to many early framework developers, and almost all frameworks include some animation facilities in their APIs—and MooTools is no exception. Most of these animation APIs address the two issues I noted, as well as other implementation details that might not be apparent to most of us.

The MooTools animation system—which is embodied by the Fx class and subclasses—is one of the oldest APIs in MooTools. As we learned in Chapter 1, MooTools started out as Moo.Fx, an animation framework for another JavaScript library. What we know now as MooTools evolved from these original Moo.Fx animation classes, which should give you an idea of just how important these APIs are.

Animation Objects

Unlike our native animation example, which uses variables and functions, the MooTools Fx system uses animation objects to perform the same set of tasks. An animation object is an instance of an Fx subclass like Fx.Tween or Fx.Morph that encapsulates the basic elements of the process, like timers and calculation functions, and controls the whole progression of the animation sequence.

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50
});

tween.start('left', 30);

Here we reimplemented our previous native animation example using an animation object that is an Fx.Tween instance. Unlike our native example, we didn't need to write a whole bunch of variables and functions, but instead simply instantiated a new animation object. Our tween object in this case contains all the necessary elements for performing the animation, and we triggered this by simply calling the start method.

Notice that the style property we're animating, left, and the value we want this item to take, 30, are passed as arguments to the start method rather than passed as options to the constructor. This is because all animation objects are generic by default: they don't describe the actual properties being animated, only the duration and speed of the animation itself. This makes animation objects reusable in a way that a single animation object can be used to animate different properties by changing the arguments to the start method.

tween.start('height', 30);
tween.start('width', 30);

However, this doesn't mean that it'll work as we'd expect. After our first call to start, the animation object will be in a working state, which means that it'll be busy performing the necessary actions to animate the height of the element.

If we call start again while the animation is in this state, the animation object will behave according to the value of its link option. If the link option has the value 'ignore', it will simply ignore the second call.

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50,
    link: 'ignore'
});


tween.start('height', 30);
tween.start('width', 30);

In this snippet we passed a link option with the value 'ignore' to our constructor. This tells the animation object to ignore any subsequent calls to start while the animation object is still busy with the previous call. Note that all Fx classes use 'ignore' as the default value for the link options, which means we don't need to include it explicitly:

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50
});


tween.start('height', 30);
tween.start('width', 30);

This example is the same as the previous one even if we didn't include the link option, because all animation objects have 'ignore' as their default link value if the option isn't specified.

Another possible value for the link option is 'cancel'. This tells the animation object that if the start method is called while it is in a working state, the new call should cancel the previous one and replace it:

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50,
    link: 'cancel'
});


tween.start('height', 30);
tween.start('width', 30);

In this snippet the second call to start will cancel the previous call, and then replace it. This means that the animation sequence that's changing the height property of the element will be stopped and the animation object will proceed with animating the width property.

Finally, animation objects can have the link option of 'chain', which stores subsequent calls to start for later processing.

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50,
    link: 'chain'
});


tween.start('height', 30);
tween.start('width', 30);

Here our animation object will store the second call to start in a queue. After the first animation sequence has finished, the animation object will then call the next one in the chain, making it possible to chain several animation sequences in order. We'll see how to combine animation sequences when we look at Fx.Morph later in the chapter.

Tween and Morph

Tween and Morph are the two main Fx classes are used for animation in MooTools. These classes have very similar APIs, with the exception of having different signatures for their start methods. Because they're inherently similar, we'll take a look first at their differences before discussing the common methods of their APIs.

Fx.Tween

We've already seen Fx.Tween. This class is used to animated single CSS properties. The Fx.Tween constructor takes two arguments, element, which is the element to animate, and options, which is an optional object that can be used to set various internal properties.

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50
});

Here we created a new Fx.Tween instance called tween. We passed the value of our square variable, which is an element, to the constructor to tell it that we're animating that element. We also passed an options object argument containing two options: duration, which sets the length of time for our animation, and fps, which sets the number of frames per second to display.

Internally, the Fx.Tween constructor passes the value of the element argument to document.id, which means we can either pass a real Element instance or simply the id of the element we're animating. The following snippet is the same as the one above:

var tween = new Fx.Tween('square', {
    duration: 1000,
fps: 50
});

At this point, we have our generic tween animation object. In order to activate the animation sequence, we need to call the start method. This method takes two arguments: property, which is a string denoting the CSS style property to animate, and value, which is the target value of the CSS property.

tween.start('height', 30);

In this snippet we're triggering the tween object to start animating the height style property of the element. The animation object will then take the initial value of the element's height property and animate it until it reaches the value of 30.

Note

The MooTools Fx system uses pixels as its main unit for numeric values, which means any number passed to the start method is interpreted as pixels.

However, the start method also has another form, which uses three arguments instead of two: property, from, and to.

tween.start('height', 20, 30);

Here the second argument, from, is used to explicitly set the initial value of the property before it is animated. In the previous form we simply used the current value of the property, but here we explicitly tell the animation object that we want to start the animation with our element's height property set to 20 before it's animated to 30.

Fx.Morph

While Fx.Tween is concerned with animating single properties at a time, Fx.Morph is used to animate several CSS properties together in the same animation sequence. This makes it useful for complex animation sequences that change an object's properties in several ways.

As with Fx.Tween, the Fx.Morph constructor takes two arguments: element, which is the element to animate, and options, which is an object containing various animation related options. Also like the Fx.Tween constructor, Fx.Morph passes the value of the element argument to document.id, so we can pass both Element instances and string ids.

var morph = new Fx.Morph('square', {
    duration: 1000,
    fps: 50
});

The main difference between the APIs of the Fx.Tween and Fx.Morph classes is the signature of their start methods. For Fx.Morph, start takes only a single argument, properties, which is an object. The keys of the properties object correspond to the CSS style properties to animate, while their values correspond to the target values for these properties:

morph.start({
    'height': 30,
    'width': 30
});

Here we called the start method of our Fx.Morph instance, passing in a properties object with two properties: height and width. This triggers the animation object to animate both the height and width properties of the element at the same time.

Like Fx.Tween, Fx.Morph also allows us to set the initial value of the property. We can do this by putting the values in our properties object arrays.

morph.start({
    'height': [20, 30],
    'width': 30
});

In this example we changed the value of the height property to an array containing two numbers. The first number will be used as the initial value of the property before animating it, while the second value will be the target value for the property.

Fx Methods and Events

As you'll notice above, both Fx.Tween and Fx.Morph have almost the same APIs, except for signature differences in the start method. These two classes actually inherit from the same base class as we'll see later, which gives them the same set of methods. Some of the shared methods, like start, are overridden by these two classes, but most of them are shared.

Note

From here on we're just going to use Fx.Tween instances in our examples in order to avoid repeating the code for both classes.

One of these shared methods, cancel, is the opposite of the start method and triggers the animation to stop:

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
    fps: 50,
});

tween.start('left', 30);

tween.cancel();

Here we called the cancel method immediately after calling start, which forces the animation sequence to halt entirely.

Of course, there are times when you don't want to completely cancel an animation sequence, but simply stop it for a bit and continue later. You can do this using the pause and resume methods:

var square = $('square'),

var tween = new Fx.Tween(square, {
    duration: 1000,
fps: 50,
});

tween.start('left', 30);

tween.pause();

setTimeout(function(){
    tween.resume();
}, 500);

In this snippet we called the pause method of our object immediately after calling start, halting the animation sequence. We then set a timer function to continue the animation using the resume method. The result is that our animation will stop for a bit, before continuing after half a second.

Animation objects, like other MooTools classes, function as event dispatchers, which means we can add event handlers that will get invoked at particular stages of the animation sequence. This is because Fx implements the Events mixin internally, which gives our animation objects the necessary event APIs.

There are three main events dispatched by the Fx classes:

  • start is dispatched right after the start method is called.

  • cancel is dispatched when the cancel method is called.

  • complete is dispatched when the animation sequence has finished.

You can add event handlers for these events using the addEvent methods we've already seen in previous chapters. Another way would be to pass the event handlers using the options argument:

new Fx.Tween('square', {
    onStart: startFn,
    onCancel: cancelFn,
    onComplete: completeFn
});

All event handlers for these events are invoked with a single argument, element, which is the element that's being animated.

Fx Internals

Without knowing about the basic principles of animation that we've discussed, the MooTools animation system might come off as overly complicated. Unlike the other internal systems we've seen so far, the main Fx animation system is composed of several interconnected classes, most of which aren't directly used. In order to understand the system, we need to take a look at these classes separately, then connect them at the end of our discussion.

The Fx Base Class

The Fx class is at the root of the MooTools animation system. This class defines the most basic components required to produce animations, and all animation-related classes inherit from Fx.

The Fx class uses all of the three built-in mutators: Options to allow passing option objects during instantiation, Events to enable animation objects to dispatch events and have event listeners, and Chain to enable the chaining of calls to the start method.

Initialization and Class Options

Initializing the Fx class method takes two arguments: subject and options. The method takes the first argument, subject, and sets it as the value of the subject property of the instance, while the second argument is passed to the setOptions method.

As we saw in the previous section, all Fx classes have three main options: duration, fps, and link. These three options are given default values by the explicit options property declared in the Fx class declaration, and these default values are 500, 50, and 'ignore' respectively.

Of special note is the duration option, which can actually take either a numeric value that represents the length in milliseconds, or one of the special "duration values." These duration values are stored in a special object called Fx.Durations:

Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};

So instead of putting duration: 250, we can use the equivalent duration value, like duration: 'short'. And since the Fx.Durations object is public, we can augment it directly to define our own duration values.

The step Method

If you look back at our native animation example, you'll see the function called step, which is used to control the animation operation. We call this function the animator function, and it's in charge of computing the amount of change to be applied for a particular frame as well as stopping the animation when the desired value has been reached.

The Fx class implements the animator method that's used by all animation objects: step. This method is called at each frame of the animation, and like its native counterpart, it performs the necessary calculations for each frame as well as checking whether to stop the animation when it's done.

Unlike our native step function, which uses the actual values of the animated property, the step method takes a different approach that uses the elapsed time. At the start of the animation sequence, the current timestamp is stored in the time property of the object. When the duration of the animation (which is expressed in milliseconds) is added to this timestamp value, we get another timestamp value, which is the target time when the animation will be completed.

When the step method is called, it checks the current time to see if it's less than the target time. If it is, the animation should still be in progress, and should therefore continue. However, if the current time is greater than the target time, the animation should be done and the step method will finish the animation sequence.

This time-based technique is also used for calculating the amount of change that needs to happen at a particular frame. In our native example, we simply divided the total amount of change by the number of frames and incremented the property value for each frame. In contrast, the step method computes the value based on the time elapsed using special equations called easing equations.

Timers

While the animator function is concerned with changing the value of the property per frame, the timer function is concerned with keeping the animation sequence moving by invoking the animator function at regular intervals.

In our native animation example, we created a single function called step, which we then ran periodically using the setInterval function. The step function therefore acted both as the timer and as the animator. In other words, the function that's in charge of changing the properties is the same function that gets called repeatedly by the setInterval function. If we were to animate multiple objects this way, we'd end up with a different timer function for each object we're animating. Because each object would have its own periodical function, we could say we have multiple timers.

MooTools, on the other hand, doesn't merge the animator and timer. Instead, MooTools creates private timer functions that call the step method of the animation objects. All timers are private and can only be accessed by the Fx class. The startTimer method of the Fx class is the method used to associate the current instance to a particular timer, while the stopTimer method is used to remove this association. These two methods are used by the other Fx methods, and are never actually invoked outside the class.

MooTools will group animation objects using the values of their fps option. All animations that run at 50fps, for example, will share the same timer function, and the same is true for all other animation objects that share the same fps value. Because a single timer function can be used for multiple animation objects, we can say that MooTools uses unified timers.

The use of unified timers gives the MooTools Fx system some great benefits. First, using unified timers minimizes the problem of multiple timers blocking each other due to JavaScript's single-threaded nature. Because multiple animation objects can share a single timer, the number of timer functions vying for execution time is lessened, which means less execution conflict.

The second—and perhaps more important—benefit of unified timers is animation synchronization. With multiple timers, only a single timer function (and therefore a single animator function) can run at a time, which means that multiple objects being animated won't have synchronized animations. In contrast, unified timers enable several animator functions to run for each pass of the interval, synchronizing the change for all objects.

The start Method

The Fx class implements the main start method that's used by all Fx subclasses. Some subclasses will override this method in order to add class-specific functionality, but these overriding methods will still call the original start method implemented by Fx.

The first thing the start method does is to check whether the animation is currently running. If it isn't, start proceeds to set various flag properties for the animation. These include items like the initial value and the target value for the property being animated, as well as the easing equation to use for the animation. The start method then calls the startTimer method of the instance, which associates the current instance to a private timer function. The startTimer method also saves the current timestamp that will be used by the step function for its calculations. Finally, the start method calls the onStart method, whose job is to dispatch the start event using fireEvent.

However, if the start method is called while the animation object is still busy with a previous sequence, it will either ignore or store this new call, or cancel the previous one and replace it with this new call—a behavior that's dependent on the value of the link option.

CSS Animation

As we've seen, the Fx base class is mostly concerned with setting up a framework for the basic elements of animation. Fx itself is very important in the MooTools animation system, but it remains incomplete because it does not implement the higher-level parts.

In order to make Fx usable, it has to be subclassed, and new methods that handle the actual property computation and change need to be implemented. Fx is actually very flexible in that you can subclass it to handle any kind of animation. Luckily, MooTools does the grunt work for us by including an Fx subclass that handles CSS style animation—the most common animation done on the DOM—called Fx.CSS.

The Fx.CSS class implements all the necessary methods that are used for CSS style animations. These include basic methods like render and serve, which are concerned with the actual computation and setting of the properties using setStyle, and more complex methods like search, which is used to parse the document's style sheets to find default values for CSS class declarations.

The most interesting part of Fx.CSS, though, is the set of parsers used for computing the changes for CSS style values. There are three default parsers for Fx.CSS: String, Number and Color. These parsers, which are stored in Fx.CSS.Parsers, are used to parse values that will be employed in the animation sequence, and turn them into JavaScript objects that can be used in computational operations.

Like Fx, though, Fx.CSS is still unusable on its own. The API for Fx.CSS is a bit unpolished, so subclasses are needed to add a more refined abstraction for the class.

MooTools provides two of these abstractions: Fx.Tween and Fx.Morph. These two classes are subclassed from the Fx.CSS class, and implement the abstractions for tweening and morphing elements. We've already seen them at work, and I think we all can agree that they give us a very elegant API for working with CSS animations.

The Wrap-Up

Animation is usually seen and not read, but we did learn a thing or two about it in this chapter. We've seen what animation actually is, and what elements that come together in the animation process. We also learned a little about CSS styles and JavaScript timers, and the roles they play in animation. Finally, we discussed how to do JavaScript animation using native JavaScript and the MooTools Fx classes.

With this chapter, our tour of JavaScript and MooTools on the browser has ended. In the next chapter we'll break free from the window, from the DOM and from elements and visuals.

So put on those riding boots and mount your space horse, as we explore JavaScript beyond the browser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset