Managing animations with Gimme (a fade demo)

Animation can be a tricky business in Javascript, particularly if the animation is invoked by some user action (such as moving the mouse over an element). Users cannot be trusted to behave in a calm, rational manner. For instance, instead of clicking the mouse button just once, they'll click it 25 times. Instead of pressing a key just once, they'll mash the key a dozen times in a row. Instead of smoothly mousing from one element to the next, they'll frantically move the mouse around as though it's being chased by a hungry cat.

If your animation is invoked by a user action like those described above, then there is good chance that the animation may come off looking sort of silly (at best) or even causing seizures (at worst). I've written animations before, that didn't account for unpredictable user behavior, and the result was a chaotic mess of flickering DOM elements. An animation to be sure, but not the one I was aiming for.

No, users should not be trusted, and we should never fall into the trap of letting ourselves believe they can. Of course, this makes our job as UI designers more difficult, but that's no reason to whimp out!

Gimme to the rescue

Gimme has an extension called Gimme Effects which, when combined with the Gimme Animation Module offers a sophisticated set of tools for creating and managing Javascript-based animations. Besides several, useful, stock effects, Gimme also offers developers the ability to create custom animations quickly and easily (but that's another blog entry).

Focus on the fade!

One of the stock effects available in Gimme Effects is the fade (in, out, or to/from arbitrary values). It's super easy to invoke a fade too. Take the following:

g('#nav > a').fadeOut('quickly');

This snippet quickly fades out all <a> elements that are direct children of the #nav element. How quick is 'quickly'? 750 milliseconds to be precise, but you're not limited. Pass in a keyword like 'quickly' or 'slowly', pass in a numeric value in milliseconds, or even pass in nothing at all (default is 'quickly'); Gimme will handle it all just fine.

Specifying more parameters

All animation effects in Gimme take at least 3 parameters (some take more). These 3 parameters, which are all always optional are:

  1. duration: positive integer value indicating the duration of the animation in milliseconds.
  2. guid: a string value that uniquely identifies this animation (you'll see later, why this can be useful). Note that this value can be null.
  3. callback: a function to be executed when the animation completes.

Suppose I wanted to alert a message that the fade operation was done. I could do the following:

g('#nav > a').fadeOut(2000, null, function() { alert('all done!'); });

There is a problem here though. Remember back at the beginning of this post, I said that users could not be trusted to behave in calm and predictable ways? Well, suppose the above code snippet is executed whenever the users clicks on a button that says "Fade the navigation." What happens if the user clicks on that button 15 times in a rows? The animation takes 2 full seconds to complete, so if the user invokes it 15 times, that's 15 different animation requests, all on the same DOM elements.

The result will be a night-club-strobe-light-flickering effect that might give your users a seizure (or a head-ache at the very least).

Unique Animations with Gimme

With Gimme, it's pretty easy to avoid this kind of flickering. You simply specify a GUID when invoking the animation, and this instructs Gimme to make sure that an animation with the specified GUID is not already running. If it is, the request to start the animation will be ignored until the animation is no longer running. Take the following example:


g('body').fadeOut(1000);
g('body').fadeOut(500);

The code above doesn't specify any GUID; the animations are not uniquely identified and therefore Gimme will happily attempt to execute both of them, even though they operate on the same element (the <body> element) and will cause flickering.

One solution, is to give both animations the same GUID, as in:


g('body').fadeOut(1000, 'FADE');
g('body').fadeOut(500, 'FADE');

When Gimme encounters the second .fadeOut(..) call, it will ignore it since an animation with the GUID "FADE" is already running. Once the animation finishes though, it will once again be eligible for invocation.

Of course, you wouldn't ever write code like what I've written above -- that was just to illustrate a point. But think again about the possibility of a user repeatedly clicking a button to invoke some animation, and you'll see how the GUID can help.

Stopping an animation

The fact that Gimme can ignore subsequent requests to start an animation that already started is nice, but it begs the question: "what if I don't want to ignore subsequent requests? What if I want subsequent requests to cause the currently running animation to abort in favor of the new request?"

Gimme can do this too; it's just not the default behavior, so it take a couple extra lines of code on your part. Nothing major though, and it's still super easy:

Gimme.Animation.end('GUID');

This instructs Gimme to abruptly stop the animation uniquely identified by the GUID, "GUID" (not a very good name for a GUID by the way), provided it exists and is running. Need to stop more than one animation at a time? No problem; just pass in a comma delimited list of GUIDS as in:

Gimme.Animation.end('FADEIN', 'FADEOUT');

Gimme will stop any animations that are uniquely identified by either "FADEIN" or "FADEOUT."

It's worth noting that all animations have a GUID, even if you don't specify one. For animations where you fail to specify a GUID, one is auto generated for you. It's a good idea to specify your own though, as this allows you to manually stop the animation at will.

.fadeTo(startOpacity, endOpacity, duration, guid, callback)

Besides .fadeIn(..) and .fadeOut(..) (which are somewhat restrictive since they're "all or nothing"), Gimme also offer .fadeTo(..). This function works like its in/out couterparts, but it allows you to specify the starting and ending opacity for the fade. As an added bonus, you can pass in null for either value. A null value for startOpacity indicates that you want the starting opacity to be the object's current opacity, while a null value for endOpacity means zero.

For example:

g('#btn1').fadeTo(null, .5, 'slowly', 'PARTIAL_FADE');

This code snippet will fade the element, #btn1 starting at its current opacity (which is most likely 1, but doesn't have to be) and decreasing to .5 (or 50%) opacity.

A working Gimme demo involving fade

To help tie everything I've been writing about together, I've created a snazzy little fade demo for all to enjoy. In it there is a #navigation <div> with a few <button> elements inside of it. Whenever the user mouses over a button, all other buttons should partially fade out. When the user mouses out of the #navigation <div> completely, all buttons should return to their full opacity.

With Gimme, this turned out to be pretty darn easy. It's only about 12 lines of code in all:

View the Gimme Fade Demo (source is visible on the page)

New to Gimme?

If you're new to Gimme, you should know that it's free and open source! Check out these links for more information:

Comment welcome.

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.