Avoiding Complex Animation Callback Hell with Storyboard

July 8th, 2013

As beautiful interactions become more and more important on the web, developers need to make sure their UI code stays readable and maintainable. I've had to create some complex animations for product demonstrations on a few recent sites, including a case study here, and Zach Roszczewski's Flaticons.

In the past, animation of multiple elements in series would usually be handled with callbacks; something along these lines:

// Step 1:
$('#some-element').animate({
    width: 100,
    height: 50
}, {
    duration: 300,
    complete: function() {
        // Step 2:
        $('#another-element').fadeOut(300, function() {
            // Step 3:
            setTimeout(function() {
                // Step 4:
                $.ajax({
                    url: 'http://google.com',
                }).done(function() {
                    // Step 5:
                    setTimeout(function() {
                        // Step 6:
                        $('#third-element').remove();
                    }, 250);
                });
            }, 300);
        });
    }
});

Using callbacks like this quickly becomes cumbersome, leading to ugly, unmaintainable code. Luckily, jQuery has had $.Deferred objects since 1.5, but they're still a bit awkward to use in long, sequential processes. (If you're new to Deferred objects, you should check out Eric Hynds great post, Using Deferreds in jQuery 1.5)

After dealing with this one too many times, I decided to make a jQuery plugin that exposes the $.Deferred objects with a natural syntax. It's called Storyboard, and you can get it now from the Octopus Creative GitHub. By using Storyboard, we can write that same code like this, instead:

// Pass an array of functions and numbers to `$.storyboard`
$.storyboard([
    // Step 1:
    function(dfr) {
        $('#some-element').animate({
            width: 100,
            height: 50
        }, {
            duration: 300,
            complete: dfr.resolve
        });
    },
    // Step 2:
    function(dfr) {
        $('another-element').fadeOut(300, dfr.resolve)
    },
    // Step 3: (numbers cause a delay)
    300,
    // Step 4:
    function(dfr) {
        $.ajax({
            url: 'http://google.com'
        }).done(dfr.resolve);
    },
    // Step 5:
    250,
    // Step 6:
    function(dfr) {
        $('#third-element').remove();
        dfr.resolve();
    }
]);

The code for Storyboard is only 34 lines; it embraces the Unix philosophy of doing one thing simply and well. Hopefully it helps you write cleaner, more maintainable animations.