CSS keyframe animations are awesome. They're one of the most powerful, versatile tools in CSS, and we can use them for all sorts of nifty things.
But they're also often misunderstood. They're a bit quirky, and if you don't understand those quirks, using them can be quite frustrating.
In this tutorial, we're diving deep into CSS keyframes. We'll figure out how they work, and see how to build some pretty swanky animations with them. ✨
Intended audience
This tutorial is written for JavaScript developers trying to become more comfortable with CSS. But it should be suitable for all developers comfortable with the basics of HTML/CSS. If you're pretty advanced with CSS, you'll probably know most of what we cover, but I do share some pretty cool and obscure stuff near the end of this post. 😄 In this tutorial, we build on the knowledge shared in “An Interactive Guide to CSS Transitions”.
Syntax
The main idea with a CSS keyframe animation is that it'll interpolate between different chunks of CSS.
For example, here we define a keyframe animation that will smoothly ramp an element's horizontal position from -100% to 0%:
Each @keyframes statement needs a name! In this case, we've chosen to name it slide-in. You can think of this like a global variable.*
Keyframe animations are meant to be general and reusable. We can apply them to specific selectors with the animation property:
(To re-run the animation, refresh the “Result” pane by clicking the icon.)
As with the transition property, animation requires a duration. Here we've said that the animation should last 1 second (1000ms).
The browser will interpolate the declarations within our from and to blocks, over the duration specified. This happens immediately, as soon as the property is set.
Timing functions
In “An Interactive Guide to CSS Transitions”, we learned all about the different timing functions built into CSS.
We have access to the same library of timing functions for our keyframe animations. And, like with transition, the default value is ease.
Looped animations
By default, keyframe animations will only run once, but we can control this with the animation-iteration-count property:
It's somewhat rare to specify an integer like this, but there is one special value that comes in handy: infinite.
For example, we can use it to create a loading spinner:
Note that for spinners, we generally want to use a linear timing function so that the motion is constant (though this is somewhat subjective—try changing it and see what you think!).
Multi-step animations
In addition to the from and to keywords, we can also use percentages. This allows us to add more than 2 steps:
The percentages refer to the progress through the animation. from is really just syntactic sugar? for 0%. And to is sugar for 100%.
Importantly, the timing function applies to each step. We don't get a single ease for the entire animation.
In this playground, both spinners complete 1 full rotation in 2 seconds. But multi-step-spin breaks it into 4 distinct steps, and each step has the timing function applied:
Unfortunately, we can't control this behaviour using CSS keyframe animations, though it is configurable using the Web Animations API. If you find yourself in a situation where the step-by-step easing is problematic, I'd suggest checking it out!
Alternating animations
Let's suppose that we want an element to "breathe", inflating and deflating.
We could set it up as a 3-step animation:
It spends the first half of the duration growing to be 1.5x its default size. Once it reaches that peak, it spends the second half shrinking back down to 1x.
This works, but there's a more-elegant way to accomplish the same effect. We can use the animation-direction property:
animation-direction controls the order of the sequence. The default value is normal, going from 0% to 100% over the course of the specified duration.
We can also set it to reverse. This will play the animation backwards, going from 100% to 0%.
The interesting part, though, is that we can set it to alternate, which ping-pongs between normal and reverse on subsequent iterations.
Instead of having 1 big animation that grows and shrinks, we set our animation to grow, and then reverse it on the next iteration, causing it to shrink.
Comments