Section G. Animations


G. Animations

Animations draw the eye of the user to the element that's animated. If that's a good thing, the animation serves a good purpose; but if not, it doesn't.

XMLHTTP Speed Meter is the only example script that uses an animation. Although it's there to provide some eye candy, it also serves a real purpose. By making the animation fluctuate within a range of download speeds, the interface conveys the message "Your download speed will be somewhere around here," without specifying an exact speed. This is very important to the clientan ISP who simply cannot guarantee an exact speed, and wants its clients to be aware of that fact.

How animations work

Animations are repeated changes to the same style of the same element. With a bit of luck, the style changes follow each other so rapidly that the user perceives the animation as one continuous movement instead of a lot of small steps.

Nonetheless, JavaScript animations may become jerky for several reasons: the script isn't fast enough; the user's computer is slow; the page has many scripts running in addition to the animation; or the animation uses too many tiny steps. If perfect animations are an absolute requirement for your site, Flash remains the best tool.

Animations are one area in which changing the style property is better than a class change. If XMLHTTP Speed Meter used class changes, I'd have to define dozens of classes: one for each possible animation step. That would make the CSS presentation layer extremely complicated.

XMLHTTP Speed Meter uses two nested <div>s. The outer one contains the grey background image, and the inner one the black image. Initially, the inner <div> has width: 0 and is thus invisible. The animation script changes the width of the black <div>, and since its background has exactly the same position as the outer background, it appears to become a black meter running over a greyed background:

<div >     <div >     </div> </div> div.grey {     background: url(pix/bg_meter_grey.gif); // grey bg     height: 23px;     width: 280px; } div#meter {     background: url(pix/bg_meter_black.gif); // black bg     height: 23px; } 


[XMLHTTP Speed Meter, lines 70-73]

function setWidth(width) {    if (width < 0) width = 0;    document.getElementById('meter').style.width = width + 'px'; } 


Figure 9.4. Initially, the meter has width: 0 (invisible); later, it has width: 56px and obscures part of the grey background.


Changing the width is pretty easy (as long as you remember to append a correct unit). However, the width shouldn't change once, but many times over. How do we cleanly issue a lot of commands to change the width?

setTimeout and setInterval

In order to create a proper animation, you need either setTimeout or setInterval. Before discussing the use of these methods, let's take a peek at the problem they solve.

If you command the browser to change widths multiple times without any pause, it will change the on-screen rendering only after the entire script has been run. Suppose we want to animate a change from 0 to 56px width, with 14px intervals:

   function changeWidths() {      setWidth(14);      setWidth(28);      setWidth(42);      setWidth(56);  } 


Now the browser faithfully changes the width of the element to 14, 28, 42, and 56 pixels. However, it doesn't show any of these changes until the function has ended. Therefore, as far as the user is concerned, the black bar is invisible at first (width: 0), and then all of a sudden it has a substantial size (width: 56px), without any steps in between. This is no animationyou might just as well have set width: 56px immediately.

We have to pause between the steps to give the browser the chance to catch up and to show every single step on the screen before proceeding to the next one. We have to time our commands: do setWidth(14), wait, do setWidth(28), wait, etc.

As we discussed in 6E, JavaScript contains two methods that allow you to time your commands: setTimeout and setInterval. They come in especially handy in animations.

Using setTimeout

We're going to use setTimeout to define a slight waiting period between the width changes. (Why not setInterval? We'll discuss that later.)

function changeWidths() {     setTimeout('setWidth(14)',100);     setTimeout('setWidth(28)',200);     setTimeout('setWidth(42)',300);     setTimeout('setWidth(56)',400); } 


Now the animation works. The browser is commanded to run setWidth() four times: once with argument 14 after 100 milliseconds, once with argument 28 after 200 milliseconds, etc. As far as the browser is concerned, after executing the function once, it considers the function ended, and shows the results on-screen.

Now the user sees a simple animation, each step of which makes the black bar 14 pixels wider, with a 100 millisecond pause between each step. The style change has become a true animation.

Using the Right Interval Time

An important part of creating a good animation is choosing an appropriate interval for your steps. In theory, the smaller the time, the better the animation, since small steps make an animation more fluid. Unfortunately, if you make your steps too small, browsers start to have trouble.

10 milliseconds seems to be a practical limit; it's about the smallest delay time browsers can handle. But even if you stay above this limitsetting, say, 500 animation steps with a 20-millisecond intervalyour script may use too many browser resources, since it runs 500 times in a 2-second period. Of course, the exact behavior of the browser depends on the speed of the computer it runs on, so some browsers might still show such an animation correctly.

I generally use a delay time of 50 milliseconds at minimum, and restrict the number of steps to a moderate amount, say 10 to 50.


When creating an animation, I always allow the client to set the exact animation time. Clients frequently think an animation is slightly too slow or too fast, and by allowing them to modify one variable by themselves, I save myself valuable development time.

So let's add a variable moveTime, which defines the pause between the animation steps:

var moveTime = 100; // in milliseconds function changeWidths() {     setTimeout('setWidth(14)',moveTime*1);     setTimeout('setWidth(28)',moveTime*2);     setTimeout('setWidth(42)',moveTime*3);     setTimeout('setWidth(56)',moveTime*4); } 


That's a useful addition, but the function is still quite ugly. Looking for ways to further sanitize it, we see that what we're really doing is giving a repetitive command, and such commands can be worked into a for{} loop:

var moveTime = 100; // in milliseconds function changeWidths() {     for (var i=1;i<5;i++) {             setTimeout('setWidth(' + (i*14) + ')',moveTime*i);     } } 


This is much better. i loops from 1 to 4. The width that the black meter should be set to is always i*14 (14, 28, 42, 56), and the timeout time is always moveTime*i (100, 200, 300, 400). Now the animation function has become much more standardized, and all timeouts are set at once, reducing the clutter in your script.

setTimeout or setInterval?

Why do I use setTimeout, not setInterval? In my opinion, the two methods, though similar, are meant for different situations, especially when it comes to animations. Before continuing, let's review the difference:

setTimeout('theFunction()',100); setInterval('theFunction()',100); 


The first line means "Execute function theFunction() 100 milliseconds from now." The second line means "Execute function theFunction() 100 milliseconds from now, and continue executing it every 100 milliseconds."

setInterval is best for animations that continue for an indefinite time (for instance, until the user does something). setTimeout, on the other hand, is best suited for animations that have a fixed start and end point.

The animation that moves the speed meter to a new value has a fixed start point (the old value) and a fixed end point (the new value.) Therefore this animation requires setTimeout.

Once the XMLHTTP Speed Meter has arrived at its new value, however, a second animation kicks in. This is the fluctuation that continues until the user does something. Since the fluctuation doesn't have a fixed start and end point but should continue until further notice, this animation requires setInterval.

Examples

XMLHTTP Speed Meter initializes the two kinds of animations. When a new speed arrives from the server, moveToNewSpeed() is called. We'll ignore the calculations and focus on setting and cancelling the timeouts and intervals:

[XMLHTTP Speed Meter, lines 78-86, condensed]

function moveToNewSpeed(Mbit) {     for (var i=0;i<animationSteps.length;i++)             clearTimeout(animationSteps[i]);     animationSteps.length = 0;     // calculations     clearInterval(fluctuationInterval); 


As soon as moveToNewSpeed() is called, it clears all timeouts and intervals that may exist; all animations should stop what they're doing and await new orders.

All timeouts are stored in the animationSteps[] array. This is done in order to easily remove them in the clearTimeout routine at the start of this function. The function cannot be sure in advance of the exact number of timeouts, since this varies with the start and end points of the desired animations. Storing them in an array is therefore the best solution; the array can become as long as necessary.

[XMLHTTP Speed Meter, lines 90-94]

[View full width]

do { animationSteps[timeoutCounter] = setTimeout('setWidth(' + (pos*7) + ')', timeoutCounter*moveTime); timeoutCounter++; } while ([perform some calculations])


Now it calls the necessary setTimeouts in a way similar to the changeWidths() function we discussed previously. timeoutCounter counts the timeouts that are already set, so that every timeout gets its own entry in the animationSteps array. pos*7 is the desired width, so we feed this value to setWidth(). We already discussed the do/while() loop in 5H.

[XMLHTTP Speed Meter, lines 96-97]

setTimeout('initFluctuation()',timeoutCounter*moveTime); } 


Then the script sets a last timeout to initialize the fluctuation. timeoutCounter has increased since the last timeout, so initFluctuation() is called after the last time setWidth() has been called, and the fluctuation takes over seamlessly:

[XMLHTTP Speed Meter, lines 102-107, condensed]

function initFluctuation() {     // calculations     fluctuationInterval = setInterval('fluctuate()',fluctuationTime); } 


initFluctuation() performs some calculations, and then sets an interval that calls fluctuate() every once in a while. The exact time between fluctuations is set by the client and stored in fluctuationTime:

[XMLHTTP Speed Meter, lines 109-116, condensed]

function fluctuate() {     // calculations     setWidth(currentFluctuation*7); } 


fluctuate(), finally, performs some more calculations and calls setWidth(). This function continues to be called until new data arrives from the server and the interval is cancelled by moveToNewSpeed().

In this way, both animations are set and cleared in the most efficient way possible.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net