Wednesday 21 December 2011

jQuery Group Animate

.animate

jQuery has a great method for dealing with animations called animate. While it works for most things I've been finding its requirement of an element selection to work on a bit restricting.

Animations these days are not always just about elements animating individually. Animations that use the canvas element for example tend to be more about changing values through time and using them to draw the scene onto the canvas each frame.

gibsy.com's parallax-fuelled listing page, from webdesignerwall.com's article on parallax scrolling effects

The most obvious use of this approach is the parallax effect seen on many sites recently. (While this is not technically animating through time it is the same principle of breaking a process down into 'frames'.)

Steph band's events.frame is a nice example of how to approach this (and served as my starting point for building a solution).

My requirements

So a more thorough view of what my requirements were is

  1. Animations don't have to just run on elements
  2. Animations are split down into 'frames', which is where the code will run
  3. Code inside these frames has access to information about the animation and its progress
  4. Code that runs in the frames can sit in any place in your codebase

We can solve two & three by using the .animate method's step callback, which runs whenever the timer inside an animation is fired & gives the information needed.

The other two requirements need some more thought and a bit of hacking ;-)

Solving the rest

Using .animate without a selection

The first point made above requires a bit of a hack. Turns out you can create a dummy element to use as the selection.

var $fakeElem = $('<div style="width:0px;"></div>');
...
$fakeElem.animate({'width' : '100px'}, opts);

The element isn't added to the DOM so isn't even rendered by the browser, we're only interested in what goes on inside the animation's step callback.

Freeing up the usage

The fourth requirement, to allow the code that runs inside the callback, is solved by placing a custom event inside the step.

opts.step = function (now, fx) {
$(document).trigger('frame', { 'fx' : fx });
};

This fires a 'beacon' every time the callback runs which any code can attach itself to. Any code bound to it will have full access to all the information it needs about that step of the animation.

The code

I've put some code up on Github to demo the approach. Bear in mind this isn't a jQuery plugin, just a demonstration of an approach I've found useful.


To see the code in action, have a look at it's Github page.

Why use .animate?

To conclude I want to answer one of the most obvious questions about the above approach: 'why use .animate at all?'. Most of the code doesn't really need to be written in jQuery and we're not using an element selection so why bother?

  • .animate is really nice to use.
  • It allows users to be pretty sure when their animations will end due to it checking the progress against timestamps at each step.
  • It provides your code with all the information you need when working on a per-frame basis.
  • You can plug different easing routines into it.
  • As long as you use it according to the docs the jQuery team will improve it, you don't have to. (For example, the requestAnimationFrame method used by modern browsers to improve animation was added soon after it became available.)

Ultimately though, you don't need to. All the main bits, from the use of custom events to that of jQuery can be swapped out for others, I just wanted something with all the benefits above that I could use now.