Choosing a technology stack for web animations can be challenging. In this blog entry, I discuss my search for a jQuery replacement and introduce WAAPI, a new—and better API—for web animations.
My search for a jQuery replacement
jQuery has become outmoded, leading me to switch to React about a year ago. Still, I missed some of jQuery’s elements, including its capabilities to add and remove elements from screens and to set callbacks while animating. After all, these tasks are impossible with CSS animations and transitions alone.
To make up for these lost functions, I explored various animation libraries and wrote my own animation methods—neither of which was ideal. For one, animation libraries are too large to import for one simple automation. Plus, animation code is time-consuming to write and difficult to maintain, and each library used necessitates learning a new API. I also tried hacking my way through while using setTimeout, requestAnimationFrame or setInterval (asking the browser to redraw whenever possible) and began relying on react-transition-group.
Unfortunately, many issues remained:
There were no callbacks: no way of knowing when animations started or finished.
Animations could not be controlled (changed).
The functionality was based on using timeouts.
The library was no longer fully supported.
Meet WAAPI
A post by Dan Wilson provided me with the solution I sought—an introduction to a whole new web animation API (currently under development): Web GUI animation API (WAAPI). WAAPI provides a way to create CSS-like animations and allows for the manipulation of animations in real time (even the changing of timelines). Specifically, method document.querySelector('.block').animate which attaches animations to DOM elements and return animation control objects.
Even better, WAAPI is incredibly simple to learn, akin to CSS animations (see below for examples). Using this API frees me from relying on timeouts or intervals, which results in cleaner, easier-to-maintain code.
Already supported by Chrome and Firefox, WAAPI will soon also be fully usable in Safari and Edge (in Safari, the feature can be enabled by toggling a flag in “Settings”). Fortunately, a reliable polyfill is also available, meaning we can use WAAPI now.
Web animations API
Source: https://caniuse.com/#feat=web-animation
WAAPI keeps getting better and better…
Below are a few of the WAAPI features currently in development:
getAnimations method: This method works partially in Chrome and Firefox and, with polyfill, will return all animations currently attached to elements. One feature currently available only on Firefox: getanimations will return animations created using WAAPI and CSS, enabling us to manipulate animations as if they were created with WAAPI.
Timelines: Currently, WAAPI uses page timer to run animations. Yet, in the near future, it will be possible to transition to whichever timeline we desire for animation objects. We will be able to easily manipulate animations by the amount the page is scrolled or the length of text typed in the text field. No more writing messy calculations.
Accessibility: In the future, we will be able to offer users the ability to change the playback rate of all page animations or to stop them completely. After all, many users feel nauseous when viewing moving objects. Because there will be native browser functionality, we won't need to include any large additional libraries (even the polyfill takes up only 6 KB).
How to use WAAPI
Switching from CSS animations to WAAPI is very simple. Here are a few examples of the process:
Example 1:
Let’s say we have a block on our page that we want to animate:
<div class="block"></div>
Let’s say we want that block to fade in and out, and the animation should take two seconds to finish. To achieve this, we would write something like this in our CSS code. First, we declare the key-frames that make the actual animation:
@keyframes fade-in-out {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
Then, we add this animation to the block we want to animate:
.block {
animation: fade-in-out 2s;
}
Now, we have a block that fades in and out in two seconds.
Next, let’s see how we would achieve this same result using WAAPI. Similar to the previous approach, we first need to create an object that holds our key-frames:
const fadeInOutKeyframes = [
{
opacity: 0
},
{
offset: 0.5,
opacity: 1
},{
opacity: 0
}
];
Then, we simply attach that animation to our block:
document.querySelector('.block').animate(
fadeInOutKeyframes,
{
duration: 2000
}
);
You can fiddle with it here.
That’s it. As you can see, the syntax is really similar. You describe the animation, then select the DOM element and then add the animation. The only difference is the tiny change in the animation properties’ names:
Example 2:
Let’s use the same case as above: a block that needs to fade in and out in two seconds. We create the key-frames object and add it to the DOM element. However, this time we assign the value that the .animate() method returns into a variable that will give us access to the animation object:
const fadeOutKeyFrames = [{
opacity: 1
},{
offset: 0.5,
opacity: 0
},
{
opacity: 1
}];
const childAnimation = child.animate(
fadeOutKeyFrames, {
duration: 2000
}
);
Let’s say that during the run-time, we want to change the duration of the animation, making it two times faster. We can easily achieve that via:
blockAnimation.playbackRate = 2;
Yet, I recommend the following, since we do not need to know the animation’s duration and are not changing the animation duration property. The next line of code changes the animation speed to its default value:
blockAnimation.playbackRate = 1;
You can fiddle with it here.
Example 3
Let’s say we have an element on the screen that we want to remove from the DOM with an animation. This means that we need to wait until the element fades out before removing it from the DOM. We have a simple structure, such as:
<div class=”parent”>
<div class=”child”></div>
</div>
We’ll achieve the desired result by getting the animation object and setting the callback to onfinish property. We do not need to use any timeouts or additional checks to see if the animation is finished. Plus, with this method, we get features that would ordinarily require lots of code and checks.
const parendBlock = document.querySelector('.parent');
const childBlock = document.querySelector('.child');
const keyframesFadeOut = [
{ opacity: 1 },
{ opacity: 0 }
];
const childFadeOutAnimation = childBlock.animate(
keyframesFadeOut,
{ duration: 500 });
childFadeOutAnimation.onfinish = () => {
parendBlock.removeChild(childBlock);
};
For example, what if we would need to cancel the removal of the previously mentioned block by some kind of user interaction? We could simply call:
childFadeOutAnimation.cancel();
This method would cancel the animation and return the block to the state it was in before the animation began. Plus, because we canceled the animation, it never finished. This means that the onfinish callback is never called and the element is never removed from the DOM, meaning we don’t need any additional checks or clearing timeouts to cancel that event. You can fiddle with it here.
Final thoughts on the new ruler of animation lands
When I first started this research, I didn’t expect that this new API could offer so much. In this blog post, I've only scratched the surface—there's way more to uncover and try out.
I'm excited about this new API and can’t wait to start implementing it in upcoming projects. With it, we'll discover more and more ways to utilize the API to create better interaction designs and a smoother user experience.
Resources:
https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
https://developer.mozilla.org/en-US/docs/Web/API/Web\_Animations\_API