Skip to content

Writing JavaScript Transitions

Michael Bromley edited this page Aug 14, 2014 · 5 revisions

JavaScript animations are significantly more complex to write than CSS animations, but the trade-off is that they offer virtually limitless possibilities.

This is not a guide to JavaScript animation in general, but in brief: JavaScript animations are achieved by making small changes to one or more properties of the elements on the page rapidly (usually at around 60 times per second). This is typically done by taking a function that makes the incremental changes, and then looping it via the window.requestAnimationFrame() method (or formerly the window.setInterval() method).

In Horizonal, we can specify these animation functions by using the onPageTransition and onNodeTransition options in the config object.

The functions we define as callbacks will be executed whenever we transition from one page to another. The functions will be called on both the page that we are transitioning away from, and the page that we are transitioning to.

The functions are passed pre-defined arguments:

  • type
  • page/node
  • animator

Full descriptions and properties of these can be found in the readme file. Here we will take a look at examples of usage.

The animator Object

The third argument passed to your callbacks is the animator. This is a helper object designed to simplify and optimize your JavaScript animations. It has two methods, start() and stop(), and is used thus:

onPageTransition: function(type, page, animator) {
    animator.start(function() {
        // This is your animation function.
        if (endCondition) {
            // the animation is complete
            animator.stop(this);
        }
    });
}

When you call start(), the function you pass will be put into a collection together with all the other animation functions that should be executed during this page transition (and depending on how many elements are on each page, this can be a lot). This collection of functions is then executed inside a window.requestAnimationFrame() loop to create the animation effect. When stop(this) is called, the function is removed from this collection and therefore stops executing.

Because the function you pass to the start() method will be called using requesAnimationFrame(), it will be passed a high-resolution timestamp as its argument.

Example: A Simple Fade Animation

For the first example, to keep things as simple as possible we'll only use the onPageTransition callback. The effect we want to produce is to make the page fade out when we transition from one to the next. Here is what the code would look like:

horizonal.init({
    onPageTransition: function(type, page, animator) {
        var DURATION = 500;
        var start, elapsed, opacity;

        if (type === 'toForeground' || type === 'toBackground') {
            opacity = 1;
            var fadeOut = fade('out');
            console.log('page ' + page.pageNumber + ' running fadeOut()');
            animator.start(fadeOut);
        } else {
            opacity = 0;
            var fadeIn = fade('in');
            console.log('page ' + page.pageNumber + ' running fadeIn()');
            animator.start(fadeIn);
        }

        // since the fadeIn and fadeOut effect shares 
        // almost identical logic, I'm using a bit
        // of currying to avoid repeating the code.
        function fade(direction) {
            return function(timestamp) {
                if (typeof start === 'undefined') start = timestamp;
                elapsed = timestamp - start;
                page.domNode.style.opacity = opacity;
                var progress = elapsed / DURATION;
                opacity = direction === 'in' ? progress : 1 - progress;

                if (DURATION < elapsed) {
                    animator.stop(this);
                }
            };
        }
    }
});

The above example demonstrates all the main principles involved in writing JavaScript animations:

  • determining the type of transition by inspecting the type argument
  • using the animator API to start and stop animations
  • using the timestamp argument that is made available to the animation function
  • accessing and manipulating the raw HTML element via the domNode property