Getting Started With The JavaScript Web Animation API

Adding animations to web interfaces makes pages and apps feel more responsive and interactive. A side menu that smoothly slides out of view provides a much better user experience then a menu that just disappears when you close it.

So far creating web animations was done either via CSS transitions, CSS keyframes, or an external library such as Animate.css or Velocity. Thanks to a new native JavaScript API, we are now able to freely animate any HTML element without ever having to leave our .js file.

Creating Animations

To showcase the awesomeness of the new API, let's build a super simple example, once the old-fashioned CSS way, then with JavaScript Web Animations.

The editor below contains two HTML divs that when clicked on move to the right and then change their color. The square is animated via CSS code>@keyframes, and the circle via the Web Animations API.

<h4>Click on the shapes to animate them.</h4>

<p>CSS keyframes</p>
<div id="square"></div>

<p>JS Web Animation API</p>
<div id="circle"></div>
#square,
#circle {
    width: 100px;
    height: 100px;
    margin: 10px 10px 30px;
    background-color: #2196F3;
}

#circle {
    border-radius: 50%;
}

.animate {
    animation-name: move-and-change-color;   
    animation-duration: 0.4s;
    animation-fill-mode: forwards;
}

@keyframes move-and-change-color {
    0% {
        transform: translateX(0);
    }

    80% {
        transform: translateX(100px);
        background-color: #2196F3;
    }

    100% {
        transform: translateX(100px);
        background-color: #EF5350;
    }
}
var square = document.getElementById('square');

square.addEventListener('click', function() {
    square.className += " animate";
});

var moveAndChangeColor = [
    { 
        transform: 'translateX(0)',
        background: '#2196F3'    // blue
    },
    { 
        offset: 0.8,
        transform: 'translateX(100px)', 
        background: '#2196F3'    // blue
    },
    {
        transform: 'translateX(100px)',
        background: '#EF5350'    // red
    }
];

var circle = document.getElementById('circle');

circle.addEventListener('click', function() {
    circle.animate(moveAndChangeColor, {
        duration: 400,
        fill: 'forwards'
    });
});

The code>@keyframes animation should be familiar to most developers so let's look at that first.

The CSS Approach

Our CSS animation is defined in a code>@keyframes block that represents a timeline of all the transitions. Once we have our choreography defined, we can map it to a selector via the animation property and it's options.

.animate {
    animation-name: move-and-change-color;   
    animation-duration: 0.4s;
    animation-fill-mode: forwards;
}

@keyframes move-and-change-color {
    0% {
        transform: translateX(0);
    }

    80% {
        transform: translateX(100px);
        background-color: #2196F3;
    }

    100% {
        transform: translateX(100px);
        background-color: #EF5350;
    }
}

We want the animation to start on user interaction so we will also have to create an on-click event listener that adds a CSS class to the desired element:

var square = document.getElementById('square');

square.addEventListener('click', function() {
    square.className += " animate";
});

Although it works pretty well, the CSS approach seems rather non-intuitive as we define what happens in the stylesheets, but actually start it in the JavaScript. We also have very limited control over the animation once it has been invoked. Both these problems can be solved by switching to the Web Animation API.

The JavaScript Approach

We can describe our JavaScript animation using almost the exact same transitions we used in the CSS example:

var moveAndChangeColor = [
    { 
        transform: 'translateX(0)',
        background: '#2196F3'    // blue
    },
    { 
        offset: 0.8,
        transform: 'translateX(100px)', 
        background: '#2196F3'    // blue
    },
    {
        transform: 'translateX(100px)',
        background: '#EF5350'    // red
    }
];

Each object in the array represents a state of the animation. The states are evenly distributed in time (3 states - 0%, 50%, 100%) unless we change the timing using the offset option, as we've done with the middle state.

After we've defined our animation array, we can invoke it using the animate() method. It takes as a second argument an object with the same options as the CSS animation property, although with slightly different names (e.g. animation-fill-mode is fill, animation-iteration-count is iteration, etc).

var circle = document.getElementById('circle');

circle.addEventListener('click', function() {
    circle.animate(moveAndChangeColor, {
        duration: 400,
        fill: 'forwards'
    });
});

As you can see, the JavaScript approach is much more organized with the animation stored in a variable and the animate() method used for invoking it whenever we need to.

Controlling Animations

The Web Animation API also makes it possible to easily control the playback of an animation in a number of ways. The animate() method returns an Animation object which we can save in a variable and use to refer to that animation later on.

var animation = elem.animate(transitions, options);

The interface provides us with the following methods:

  • pause() - Freezes the animation in its current state.
  • play() - Resumes the animation or restarts it if it has finished.
  • reverse() - Plays the transitions backwards.
  • finish() - Goes to the end of the animation (or the beginning if reversed).
  • cancel() - Stops playback and returns to starting state.

Below is a tiny demo with a loading indicator that loops infinitely. We've setup buttons for the different events so that you can try them out:

var spinner = document.getElementById('spinner');
var spinnerAnimation = spinner.animate([
    {
        transform: 'rotate(0)'
    },
    {
        transform: 'rotate(359deg)'
    }
], {
    duration: 1000,
    iterations: Infinity
});

document.getElementById('pause').addEventListener('click', function() { 
  spinnerAnimation.pause();
});
document.getElementById('play').addEventListener('click', function() { 
  spinnerAnimation.play(); 
});
document.getElementById('reverse').addEventListener('click', function() { 
  spinnerAnimation.reverse(); 
});
document.getElementById('cancel').addEventListener('click', function() { 
  spinnerAnimation.cancel(); 
});
<div id="spinner"></div>

<p>Try controlling the animation:</p>
<button id="pause">Pause</button>
<button id="play">Play</button>
<button id="reverse">Reverse</button>
<button id="cancel">Cancel</button>
#spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 3px solid #e2e2e2;
    border-top-color: #186aab;
    margin: 50px;
}

Properties and Events Listeners

The Animation object returned from animate() holds several useful properties that give us access to options like the current time, the playback rate, and others. Although some are read-only, most of the properties can be used as setters and getters.

You can view the JS code in the editor below to get a sense of how they work. For the full list of properties visit MDN.

var spinner = document.getElementById('spinner');
var spinnerAnimation = spinner.animate([
    {
        transform: 'rotate(0)'
    },
    {
        transform: 'rotate(359deg)'
    }
], {
    duration: 1000,
    iterations: Infinity
});

document.getElementById('half').addEventListener('click', function() { 
  spinnerAnimation.playbackRate = 0.5; 
});
document.getElementById('normal').addEventListener('click', function() { 
  spinnerAnimation.playbackRate = 1; 
});
document.getElementById('double').addEventListener('click', function() { 
  spinnerAnimation.playbackRate = 2; 
});
document.getElementById('triple').addEventListener('click', function() { 
  spinnerAnimation.playbackRate = 3; 
});
<div id="spinner"></div>

<p>Set Playback Speed:</p>
<button id="half">0.5</button>
<button id="normal">Normal</button>
<button id="double">2</button>
<button id="triple">3</button>
#spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 3px solid #e2e2e2;
    border-top-color: #186aab;
    margin: 50px;
}

In addition to that, the Web Animation API provides us with two useful event handlers for when the animation has finished or has been canceled:

spinnerAnimation.addEventListener('finish', function() {
    // Animation has completed or .finish() has been called.
    doSomething();
});

spinnerAnimation.addEventListener('cancel', function() {
    // Animation has been canceled.    
    doSomething();
});

Support and Performance

Most of the Web Animation features are freely available in Chrome and Firefox, with Edge and Safari implementations in the working (caniuse). There is also a well maintained open-source polyfill which can be used while waiting for full browser coverage.

When it comes to performance, there shouldn't be any difference compared to regular CSS transitions, as browsers use the same engine for both. If you stick to animating only properties that don't cause redraws, such as transform and opacity, animations should keep a steady 60fps rate.

Conclusion

The Web Animation API gives developers an awesome new way to create and control web animations using nothing but pure JavaScript. For animations that are invoked on user interaction or other dynamic events, this is great news since the whole animation can be done in the controller code, without having to jump to a CSS file for the actual transitions.

This article covered most of the features of the new API, but if you want to learn more here are a couple of excellent resources we highly recommend:

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Comments 2

Awesome