There are many conversations between designers and engineers that go back and forth about the performance of animations in JavaScript. Often there is an expectation of animations to run consistently (same speed, same step size, same in all browsers, etc.), and as awesome as that would be, this pushes at the limitations of JavaScript.

Many designers experience a great working animation on a demo page, and in turn, expect that the animation can be plugged into a complex JavaScript library, whilst preserving its performance.

Again, as wonderful as this would be, there are a few things that need further explanation. Today s article will look at a simple animation in 3 top libraries: YUI, jQuery, and Prototype/Scriptaculous; and show how uniformly all the animations work well on a simple page, but degrade as the page becomes busier.

Many of you probably know that there are a number of issues affecting how well an animation works in JavaScript: how busy the client machine is, how much CPU/memory your browser dedicates to JavaScript, how much JavaScript is needed to run a given page, and how much work the JavaScript is already doing; to name a few. Also, animation libraries use a combination of the setTimeout or setInterval methods of JavaScript, which are notoriously inaccurate (+/- 15ms), and/or (new Date()).milliseconds() to attempt to smooth animation, but it is fairly easy for an entire step in the animation to be skipped. The summary is that the more complex a page gets the less consistent that animations become.

I choose to compare these three libraries because: YUI is my library of choice, as it was designed by engineers for engineers; Prototype/Scriptaculous is probably the most used library (thanks to ruby on rails and cakePHP); and jQuery is the most popular designer-used library. However, I expect that GWT, Mootools, Dojo, Mochikit, and all the others to behave about the same.

---

For this experiment we are building 3 animations that increase the width of an element from 100px to 400px, and then the reverse, using each library independently. Fortunately, both YUI and jQuery use self-contained namespaces, so they dont conflict with Prototype. Here is the code required for each:

Example 1: jQuery

var isOpenedJquery = false, isJQueryAnimated = false; var jQueryCallback = function() { if (! isJQueryAnimated) { isJQueryAnimated = true; isOpenedJquery = ! isOpenedJquery; jQuery( #containerAnimateJQuery ).animate({width: isOpenedJquery ? 400px : 100px }, 1000 * globalDuration, null, function() {isJQueryAnimated = false;}); } }; jQuery( #triggerAnimateJQuery ).click(jQueryCallback);

Example 2: Prototype

var isOpenedPrototype = false, isAnimatedPrototype = false; var prototypeCallback = function() { if (! isAnimatedPrototype) { isOpenedPrototype = ! isOpenedPrototype; isAnimatedPrototype = true; Effect.BlindDown( containerAnimatePrototype , {duration: globalDuration, scaleY: false, scaleX: true, scaleFrom: isOpenedPrototype ? 100 : 400, scaleTo: isOpenedPrototype ? 400 : 100, afterFinish: function() {isAnimatedPrototype = false;}, afterUpdate: function() {YAHOO.util.Dom.setStyle( containerAnimatePrototype , height , 20em );}}); } }; $( triggerAnimatePrototype ).observe( click , prototypeCallback);

Example 3: YUI

var isOpenedYui = false; var containerAnimate = { inYUI: new YAHOO.util.Anim( containerAnimateYUI , {width: {to: 100}}, globalDuration), outYUI: new YAHOO.util.Anim( containerAnimateYUI , {width: {to: 400}}, globalDuration) }; var yuiCallback = function() { if (! (containerAnimate.outYUI.isAnimated() || containerAnimate.inYUI.isAnimated())) { isOpenedYui = ! isOpenedYui; containerAnimate[isOpenedYui ? outYUI : inYUI ].animate(); } }; YAHOO.util.Event.on( triggerAnimateYUI , click , yuiCallback);

All implementations use a click event callback on a button to trigger animating. There is a global globalDuration variable that can be adjusted on the demo page; YUI and Prototype/Scriptaculous use seconds, while jQuery uses milliseconds. The callback function first checks to ensure that we are not already animating, which is done by setting a boolean via a callback for Prototype/Scriptaculous and jQuery, and calling the isAnimated method on the Anim object in YUI. Then we use a isOpened boolean to determine whether to animate in or out. In Prototype/Scriptaculous and jQuery we call 1-off methods that runs the animation, whereas in YUI we create Anim objects and call the animate methods.

Take a look at the demo, here.

---

What I Learned