In this post we’ll explore all the different ways animate on the web, with only
CSS and vanilla JavaScript.
For simplicity sake, we’ll animate a box moving forward.
CSS animation property
Let’s start by using the CSS animation property.
This way of animating works by first describing your animation using
@keyframes,
which is a way to declare the animation frames. For example, the starting styles
can be declared under 0% or from and the ending styles with to or 100%.
After defining our keyframe, we use it in the animation property, where we
also define the animation
duration
(1s) and the timing
function
(linear, in this case).
Now let’s try doing the same thing with CSS transition property.
With CSS transitions, we no longer have @keyframes, instead we need to pass
which property we want to animate to the transition property, while also
specifying the transition duration and timing function. Whenever we change the
box style, changes to that property value will be animated.
<!DOCTYPE html>
<html><style>
.box {background-color:black;width:20px;aspect-ratio:1 / 1;transition:transform1slinear;}
.animated-box {transform:translateX(100px);}</style><body><divclass="box"></div></body><script>// We need to force a reflow here for the animation to workdocument.body.getBoundingClientRect()document.querySelector('.box').classList.add('animated-box')</script></html>
In this example, we need a JavaScript trick to make the animation work when the
document loads — it doesn’t work with pure HTML and CSS. We need to force a
reflow with
getBoundingClientRect before adding the animated-box class for the
animation to play. If we used the animated-box directly in the HTML, without
this trick, the box will not appear moving forward, it will appear already in
its destination.
requestAnimationFrame
Now let’s try using the requestAnimationFrame to do the same thing.
requestAnimationFrame is usually not the most convenient way to animate on
the web, specially in this case.
This is because requestAnimationFrame is an imperative way to do animations,
in contrast with the declarative way of CSS.
For example, there is no parameter to define the animation duration or timing
function — requestAnimationFrame just receives a callback function to be
executed before the browser updates the screen visually (which is called a
repaint), so it’s
our job to animate the box from scratch, including for how long and in what
way.
We will need to have some kind of infinite loop to repeatedly call the function
responsible for the visual changes, but we should these function calls with
requestAnimationFrame so our visual updates will only run when the browser is
ready to update the screen visually.
This usually means that our function will be called at most 60 times per
second. In this way, we can achieve a smooth animation and not waste CPU
resources/overload the browser.
In this example, you can see how many times the animation function is run in
the console:
Fortunately, there is a much more convenient JavaScript API to animate, that is
similar to how CSS animations work but much more powerful because it’s
JavaScript: the Web Animations
API.
In the example below, we replicate our animation by calling the animate
method of the DOM element and pass an array of objects that represent each step
of the animation (this is equivalent to @keyframes in CSS) and in the next
object we specify the animation characteristics such as its duration, fill mode
(as we’ve seen) etc.
We can still use it for this blog post’s purpose of animating a box moving —
we just need to modify the DOM element to make the box move inside a callback
passed to document.startViewTransition:
<!DOCTYPE html>
<html><style>
.box {background-color:black;width:20px;aspect-ratio:1 / 1;transition:transform1slinear;view-transition-name:box;}
.animated-box {transform:translateX(100px);}</style><body><divclass="box"></div></body><script>if(!document.startViewTransition){document.body.innerHTML = 'Your browser does not suppport View Transitions API yet.'}constbox = document.querySelector('.box')document.startViewTransition(()=>{box.classList.add('animated-box')})</script></html>