The Web Animations API vs. CSS
Tags: css, webdev
Published: 2021-08-25 21:11:46

**Written by [Idorenyin Udoh] (https://blog.logrocket.com/author/idorenyinudoh/)** ✏️

Web animations are a popular addition to web design, leading to the growth of many JavaScript animation libraries including [Framer Motion](https://blog.logrocket.com/framer-motion-tutorial/) and [GreenSock](https://blog.logrocket.com/animating-vue-with-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](https://developer.mozilla.org/en-US/docs/Web/API/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](https://blog.logrocket.com/css-reference-guide-transitions/).

### 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:

```css
.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.

{% codepen https://codepen.io/idorenyinudoh/pen/PomdrEZ %}

### 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:

{% codepen https://codepen.io/idorenyinudoh/pen/KKmxjBX %}

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:

```css
.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:

{% codepen https://codepen.io/idorenyinudoh/pen/eYWMYrJ %}

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:

```css
.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:

```css
.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:

{% codepen https://codepen.io/idorenyinudoh/pen/WNjYBgm %}

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:

```javascript
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:

```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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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.

{% codepen https://codepen.io/idorenyinudoh/pen/wvdmXKz %}

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.

{% codepen https://codepen.io/idorenyinudoh/pen/bGWOEqg %}

## 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](https://blog.logrocket.com/typescript-vs-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 Dashboard Free Trial Banner] (https://blog.logrocket.com/wp-content/uploads/2019/12/cpu-memory-usage.png)](https://logrocket.com/signup/)

[LogRocket] (https://logrocket.com/signup/) 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 — [Start monitoring for free] (https://logrocket.com/signup/).