Idorenyin Udoh Frontend developer and technical writer

The Web Animations API vs. CSS

7 min read 2152

Web Animations API CSS

Web animations are a popular addition to web design, leading to the growth of many JavaScript animation libraries including Framer Motion and GreenSock. However, many developers still choose to create web animations from scratch using either CSS transitions, CSS animations, or the JavaScript Web Animations API (WAAPI).

In this tutorial, we’ll cover these three methods in-depth, learning how to use each through a relevant demo. We’ll compare each method and discuss the many benefits that make the Web Animations API underrated in my opinion. Let’s get started!

CSS transitions

CSS transitions allow us to seamlessly change a CSS property from one state to another. For example, let’s say that we want to change the value of an element’s background-color property from black to red. Naturally, the change would occur immediately, however, CSS transitions give us control over certain aspects of the process.

For example, we can decide when the change will start, what properties to animate, and how long the animation will run. To be animated, the property must be included in the list of CSS animatable properties.

Properties

Let’s review the CSS properties that we need to create a CSS transition:

  • transition-property: the CSS property that we’ll apply the transition on
  • transition-duration: the length of time the transition should run
  • transition-delay: how long to wait before the animation begins
  • transition-timing-function: the speed of the transition

transition-timing-function is either linear, increasing in speed, or decreasing in speed. Typically, transition-timing-function is either a function that determines the value of the CSS property at intermediate points or a keyword that is an equivalent of the function.

You can use the transition shorthand property in place of the four properties described above:

.element {
  transition: <property> <duration> <timing-function> <delay>;
}

Demo

Now, let’s see CSS transitions in action! In the following CodePen, when a user hovers on the display, the background color will change from black to red.

See the Pen
css transitions test
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

We made a custom demo for .
No really. Click here to check it out.

Multiple animations

In the following demo, you’ll see how we can apply CSS transitions in multiple CSS properties by separating the values of the transition properties with commas:

See the Pen
css transitions test 2
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

Now that we’re familiar with CSS transitions, let’s take a look at CSS animations.

CSS animations

CSS animations let us make our animations a little more interesting. We’ll need a @keyframes CSS at-rule, which specifies the CSS rules at the start of the animation, the end of the animation, and during the animation cycle. We’ll also need a rule that initializes the CSS animation.

Properties

Let’s take a look at the properties we need to create a CSS animation:

  • animation-name: the name of the @keyframes at-rule
  • animation-play-state: specifies if an animation is running or paused
  • animation-direction: specifies the animation’s direction
  • animation-iteration-count: the number of cycles the animation will complete
  • animation-fill-mode: how the animation applies styles to the element before and after execution

If animation-iteration-count is set to infinite, the animation will play continuously. If animation-fill-mode is set to forward, the element retains the styling in the last keyframe. If set to backward, the styling of the first keyframe is applied to the element before the animation begins.

animation-delay is the time between applying and playing the animation. It is similar to transition-delay in CSS transitions. animation-duration is the amount of time for the animation to complete one cycle. Its equivalent is transition-duration.

Lastly, animation-timing-function represents an animation’s progression through its cycle. Its equivalent is transition-timing-function. Like in CSS transitions, there is an animation shorthand for CSS animations that handles these properties:

.element {
  animation: <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>;
}

Demo

To get an idea of how CSS animations work, let’s recreate the demo from above and change an object’s background color from black to red:

See the Pen
css animations test
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

You may have noticed a few key differences from CSS transitions. For one, the animation starts immediately instead of being initialized by the user. CSS animation properties can utilize the @keyframes at-rule when it is available to run elements.

However, in CSS transitions, the rule that defines the end state of the animation has to be in a different declaration than the element’s default declaration. For this reason, we used the :hover pseudo-class in the first demo.

In CSS animations, the element immediately returns to its default styling when the animation is complete. On the other hand, CSS transitions run both ways by default.

For example, when our background changes from black to red, the animation runs, and vice versa. To implement this functionality with CSS animations, we’d need to define the animation again and set the animation-direction property to reverse.

Multiple animations

To create multiple animations using CSS animations, we can enter all of the properties we want to animate in one @keyframes at-rule and use it in an animation property, as seen in the following code snippet:

.element {
  animation: color-change 2s ease-in-out 1s infinite forwards;
}

@keyframes color-width-change {
  0% {
    background: black;
  }
  25% {
    width: 200px;
  }
  100% {
    background: red;
    width: 500px;
  }
}

Another approach is to use multiple @keyframes at-rules and separate the values of the animation properties with commas. The following approach is ideal if you want to use different values for the animation properties:

.element {
  animation: color-change 2s ease-in-out 1s infinite forwards, width-change 3s ease-in 0s infinite forwards;
}

@keyframes color-change {
  0% {
    background: black;
  }
  100% {
    background: red;
  }
}

@keyframes width-change {
  25% {
    width: 200px;
  }
  100% {
    width: 500px;
  }
}

The CodePen below contains a demo for the code snippet above:

See the Pen
css animations test 2
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

You’ll notice that the width-change animation is still running when the color-change animation is completed. To avoid repetition, you can exclude some properties from the shorthand and instead specify them in one value with the individual animation-* property.

The Web Animations API

The JavaScript approach to animation, the Web Animations API, gives developers much more control and nuance over CSS animations. Although the Web Animations API may initially seem more complex when you’re setting it up, you can disassociate it from an element’s styling, making it easier to write and more straightforward than the CSS alternatives.

To animate a DOM element with the Web Animations API, we call the animate() method on the element. The animate() method has two parameters, keyframes and options. As with CSS transitions and CSS animations, the element has to be animatable.

Keyframes

Keyframes could either be an array of keyframe objects or an object with an array of values as a property:

const keyframes = [
    { opacity: 1 },
    { opacity: 0 },
    { opacity: 1 }
]

// OR

const keyframes = {
    opacity: [ 1, 0, 1 ]
}

In the first method, the keyframe objects are distributed at even points through the animation cycle. For example, in the code block above, the points will be zero percent, 50 percent, and 100 percent by default. To change the points, set an offset property in the keyframe object with a value between 0.0 and 1.0, with 0.5 being 50 percent.

Consider the following example in CSS:

@keyframes fade-in-out {
    0% {
        opacity: 0;
    }
    60% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

Using the first keyframes method in the Web Animations API, the CSS code above would look like the following:

const keyframes = [
    { opacity: 1 },
    { opacity: 0, offset: 0.6 },
    { opacity: 1 }
]

If we were to use the second keyframes method and use an array of values as a property, our CSS code would look like the following:

const keyframes = {
  opacity: [0, 1, 0],
  offset: [0, 0.6, 1] //could also be [0, 0.6]
}

In CSS, you can use a timing function for an animation, however, with the Web Animations API, you can specify a timing function for each keyframe as well as for the entire animation.

Include the easing property for a keyframe in the same way you would include the offset property. The easing property will be applied from that keyframe to the next specified keyframe:

const keyframes = [
    { opacity: 1, easing: 'ease-in' },
    { opacity: 0, offset: 0.6, easing: 'ease-out' },
    { opacity: 1, easing: 'ease-in-out' }
]

// OR

const keyframes = {
    opacity: [ 1, 0, 1 ],
    offset: [0, 0.6],
    easing: ['ease-in', 'ease-out', 'ease-in-out']
}

To include the easing value in the entire animation, we’ll include it in the options object:

options

options can be either an integer type that specifies the animation’s duration in milliseconds or an EffectTiming object that contains at least one equivalent to a CSS animation property. Let’s review the Web Animations API’s equivalents of these properties:

CSS animation property Web Animations API equivalent
animation-name id
animation-duration duration
animation-timing-function easing
animation-fill-mode fill
animation-iteration-count iterations
animation-direction direction
animation-delay delay

We’ll cover an equivalent for the animation-play-state CSS property shortly. However, there are several properties that you can include in the options object that do not have CSS equivalents:

endDelay specifies the length of the delay after the animation has ended, which is helpful when we wish to play back-to-back animations.

iterationStart specifies the point in an iteration when an animation should start, ranging from 0.0 to 1.0. For example, a value of 0.3 would mean that the animation should start at the 30 percent keyframe point.

composite specifies how an animation’s values are combined with other animations that do not have the composite property. For instance, the value add would add the next animation to the previous animation. Naturally, the previous one would have been overwritten.

iterationComposite specifies how values are built from iteration to iteration in the animation.

animate()

The animate() method returns an Animation object. Let’s consider the properties and methods made available by the object.

Properties

  • currentTime: the current point of the animation in milliseconds
  • effect: the AnimationEffect object, which is a KeyframeEffect object
  • finished: returns a promise that resolves when the animation has ended
  • id: the id used to identify the animation
  • pending: the animation is waiting for an operation to complete
  • playbackRate: the playback rate of the animation
  • ready: returns a promise that resolves when the animation is ready to play
  • replaceState: returns the replace state of the animation
  • startTime: the time at which the animation is scheduled to begin
  • timeline: the timeline associated with the animation
  • playState: specifies whether the animation is playing or not

Methods

  • cancel(): clears the KeyframeEffect created by the animation and ends its playback
  • finish(): forwards to the end of the animation
  • pause(): pauses the animation
  • persist(): persists the animation to the element when the browser would have removed it
  • play(): starts or resumes the animation
  • reverse(): plays the animation from the end to the beginning
  • updatePlaybackRate(): sets the animation’s speed after it has already started
  • commitStyles(): commits the styling of the animation’s end state to the element

Other events for this object include cancel, finish, and remove events.

Demo

Let’s recreate our animation from earlier using the properties, methods, and options of the Web Animations API. Our JavaScript file should look like the following code block:

const element = document.querySelector('div');

const keyframes = {
    background: [ 'black', 'red' ]
}

const options = {
    id: "color-change",
    duration: 2000,
    delay: 1000,
    easing: "ease-in-out",
    iterations: Infinity,
    fill: "forwards"
}

element.animate(keyframes, options);

To play an animation continuously, we use the JavaScript keyword Infinity as the value for the iterations property in the options object.

See the Pen
web animations api test
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

To create multiple animations, we can include multiple properties in one keyframes object. To use different values for properties in the options object, we could create multiple keyframes and options objects and use the animate() method multiple times on an element.

See the Pen
web animations api test 2
by Udoh Idorenyin (@idorenyinudoh)
on CodePen.

Advantages of animating with WAAPI

Now that we understand how to animate using CSS transitions, CSS animations, and the Web Animations API, let’s note some key advantages of using the Web Animations API that are frequently overlooked.

For one, with CSS, we can only use one timing function for the entire animation. However, with the Web Animations API, we can specify easing properties for different keyframes and the entire animation in the options object.

With the Web Animations API, we can add several animations to an element using the currentTime and startTime properties of the Animation object and the Animation events. In CSS, you must either overwrite one animation with another, play all of them together, or add another animation to the same element using JavaScript.

The composite property on the options object in the Web Animations API allows us to indicate to the browser how we want successive animations to run on an element. CSS doesn’t have an equivalent.

Conclusion

The properties, methods, and events in the Web Animations API make it easy to customize animations on the web. In my experience, non-complex animations are better implemented in CSS while more complex ones are better in JavaScript.

Admittedly, animating from scratch can get very difficult. You may want to consider third-party animation tools like Flow, which can export animations into formats like the Web Animations API.

I hope you enjoyed this tutorial. Happy animating!

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web apps — .

Idorenyin Udoh Frontend developer and technical writer

Testing accessibility with Storybook

One big challenge when building a component library is prioritizing accessibility. Accessibility is usually seen as one of those “nice-to-have” features, and unfortunately, we’re...
Laura Carballo
4 min read

Leave a Reply