Friday, December 2, 2011

Animating in the browser

With this blog post I wanted to give a quick overview about how you can animate inside the browser and give some advice on best practices.


There are several ways to animate inside the browser:
  1. Continuously change screen from javascript
    • using setTimeout / setInterval
    • using requestAnimationFrame
  2. Using the layout engine
    • CSS3 Animations
    • CSS3 Transformations
Using the layout engine for your animations has several benefits. You declare your animations in a declarative fashion and the browser can find an efficient way to display that animation.




Let's take a look at an example. Let's say we want to move a div from position A to position B in a specific time. Something like this:






If we are doing this in Javascript we would be doing something like this: building a function, which moves the div a little and calling that function repeatedly until we reach the end position.
This is what the code would look like:


$(document).ready(function(){
    $("#button").click(function(){
       
        var time = 1000; // animation time in ms
        var start = new Date().getTime();
        var end = start + time;
        var left = 0; //start position
        
        window.setTimeout(animate, 10);
        
        function animate(){
            var now = new Date().getTime();
            left = (now - start) / 10; //update position
             $("#test").offset({top: 0, left: left});
            if(now < end)     
            {
                window.setTimeout(animate, 10);
            }else{
                $("#test").offset({top: 0, left: 100});
            }
        }
        
    });
});




Here is a small jsfiddle with a complete example.


The problem:
This can not be optimized by the browser / layout engine, because it does not know about the whole movement. It only knows about the small fraction of the movement.
Also we need the JavaScript VM running as well as the layout engine, which will consume a lot of CPU cycles. This might be okay for desktop, but it`s bad for mobile.
On mobile we are running on battery with slow CPUs, so we don`t want to do unnecessary things.


So for simple transitions of your UI why do them in JavaScript at all?


The solution:
If we specify the complete transition in a declarative way the browser could take care of optimizing it for us. This is what CSS3 Animations and CSS3 Transitions are about. Instead of updating the position in a "loop" we simply say move from here to there in some time. In CSS3 (for a webkit browser) this looks like this:


.move {
 -webkit-animation-name: moveAnimation; /*name of the animation */
 -webkit-animation-timing-function: ease-in-out; /* timing function */
 -webkit-animation-duration: 1000ms; /* duration for the animation */

}

@-webkit-keyframes moveAnimation {
    from { -webkit-transform: translateX(0); }
    to { -webkit-transform: translateX(100px); }
}

With the keyframes you can control any point in the transition and you can use even more than one css property. Many mobile browser can take something like this and just run it on their GPU, so we don't get any CPU what so ever. This produces very smooth and nice looking animations.


Now you have a basic idea how the two different approaches work. Lets quickly remind ourselves what the pros and cons are:




Animating in JavaScript
  • JavaScript VM needs to run
  • Can not be optimized
  • Good control over every detail
  • Inefficient
Animating with CSS3
  • Only Layout engine needs to run
  • Can be heavily optimized, executed with hardware support
  • Complex updates are hard to write


requestAnimationFrame vs. setTimeout
If you are animating with setTimeout or setInterval you should be considering using requestAnimationFrame. It has two main advantages: It runs synchronized with the display frame rate and it does not run if the webpage is not visible.

If you are using setTimeout for animating your animation loop is even running if the user is currently looking at another tab. This is why browser vendors added requestAnimationFrame so that your code would not be executed if it does not need to. Also you may be calculating things which will never be drawn on screen, because you don`t know when the browser will actually paint the DOM.

Paul Irish has written a nice post about requestAnimationFrame, I encourage you to read it if you want to know more.




Some advice
If you want smooth running animations consider using CSS3 Animations / CSS Transitions. They run much smoother than handwritten animations on many devices, but they can only be used if you are animating DOM Objects.

If you want to animate the content of a canvas element you need to do your updates in javascript using requestAnimationFrame. This is the way canvas is ment to be used and this is fine.

CSS3 Animations and CSS3 Transitions are mostly the same, but the syntax for transitions can be quite clunky if you want to transition over multiple elements.

Depending on your target platform you should be using different approaches. Webkit animations are very good supported with all iOS devices, but with android you should be using CSS3 transition. They work more reliably across android devices.

If you want to see some animations in action take a look at mgwt. 




















No comments:

Post a Comment