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 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.
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 ontransition-duration
: the length of time the transition should runtransition-delay
: how long to wait before the animation beginstransition-timing-function
: the speed of the transitiontransition-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>; }
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.
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 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.
Let’s take a look at the properties we need to create a CSS animation:
animation-name
: the name of the @keyframes
at-ruleanimation-play-state
: specifies if an animation is running
or paused
animation-direction
: specifies the animation’s directionanimation-iteration-count
: the number of cycles the animation will completeanimation-fill-mode
: how the animation applies styles to the element before and after executionIf 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>; }
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
.
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 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 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.
currentTime
: the current point of the animation in millisecondseffect
: the AnimationEffect
object, which is a KeyframeEffect
objectfinished
: returns a promise that resolves when the animation has endedid
: the id
used to identify the animationpending
: the animation is waiting for an operation to completeplaybackRate
: the playback rate of the animationready
: returns a promise that resolves when the animation is ready to playreplaceState
: returns the replace state of the animationstartTime
: the time at which the animation is scheduled to begintimeline
: the timeline associated with the animationplayState
: specifies whether the animation is playing or notcancel()
: clears the KeyframeEffect
created by the animation and ends its playbackfinish()
: forwards to the end of the animationpause()
: pauses the animationpersist()
: persists the animation to the element when the browser would have removed itplay()
: starts or resumes the animationreverse()
: plays the animation from the end to the beginningupdatePlaybackRate()
: sets the animation’s speed after it has already startedcommitStyles()
: commits the styling of the animation’s end state to the elementOther events for this object include cancel
, finish
, and remove
events.
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.
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.
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!
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.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. 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 and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.