Anthony Dillon
on 16 March 2021
In our team, we run “masterclasses” every few weeks, to share knowledge throughout the team.
Similar to Robin’s post on regex basics, here’s the contents of the masterclass on “CSS animations” that I just presented to the team.
The fundamentals
A simple `transform` example:
.btn:hover {
transform: translateY(-40px);
}
This just jumps from one position to another without animation. But shows how you have translated states using CSS.
Add transition to add animation will cause the state change to animate.
transition: transform 250ms;
Transitions require two values, property name and duration. The property name is the CSS property you would like to animate.
You can add multiple with a comma-separated syntax
transition: transform 250ms, opacity 400ms;
This allows you to be specific with the properties you want the browser to animate and provide unique attributes for each. In the above example, the duration is different between the transform and opacity.
There is a special wildcard property called “all”. This property is a catch-all for all CSS properties. It can be tempting to use “all” but it is not recommended, as your product evolves, you (or someone on your team) will likely end up updating this code at some point in the future. Resulting in an unexpected animation or poor performance.
Timing functions
The transition-timing-function
defines easing across the animation timeline.
- ease-out: enters fast and eases at the end. Commonly used when something is entering from off-screen.
- ease-in: eases in and then speeds out. Useful when moving something beyond the bounds of the viewport.
- ease-in-out: is a combination of the previous two timing functions. Should be used for looping elements.
- ease: has a small amount of acceleration and a lot of deceleration. The default value, which is good as it is good in many cases. Mainly things that stay within the viewport.
- Custom timing function can be provided with a cubic-bezier function – Which takes four numbers, representing two control points. Feel free to play around with cubic-bezier.com.
Animation performance
To keep animations to 60fps the browser has 16.6ms for compute and repaint. If the browser cannot do that in that amount of time your animation with appear janky.
Some CSS properties are very expensive, for example, height as this has a knock-on effect by moving other elements.
Others are somewhat expensive, for example, background-color
. This does not affect layout, but they do require a repaint on every frame, which isn’t cheap.
The two properties that are very cheap to animate are transform
and opacity
. If an animation currently tweaks a property like width or left, it can be improved by moving it to a transform.
Hardware Acceleration
When you animate the transform
or opacity
properties of an element. The browser will try to optimise the animation by transferring everything to the GPU as a texture.
This can result in slightly different rendering especially noticeable in text. When the animation reaches the end the element is passed back to the CPU to render with can cause unwanted movement.
will-change
to the rescue. This property allows us to hint to the browser that we’re going to animate this element, and that it should optimize for this case.
will-change: transform;
The GPU will handle the rendering all the time so it will no longer swap from CPU to GPU and back again.
Delays
Animation delays define the time the animation waits before starting. This is often useful when animating multiple elements and you want to space the animations across a timeline.
animation-delay: 2s;
Respecting motion preferences
The motion preferences are an OS-level setting. Users can toggle to express a preference for less motion. Let’s apply those lessons here, by disabling animations for those that request them to be reduced:
@media (prefers-reduced-motion: reduce) {
.btn {
transition: none;
}
}
Keyframes
You can create your own animations with much more control using the animation keyframes.
@keyframes infinite-spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
To apply this animation to an element:
.animate {
animation: infinite-spinner 2s infinite forwards;
}
An animation property requires an animation-name and a duration. Should interesting optional values are:
animation-fill-mode
which defines what values are applied by an animation outside the time it is executing. For example, forwards leave the element at the end of the animation instead of returning it to the beginning which is the default behaviour.steps
: The steps() function controls exactly how many keyframes will render in the animation timeframe.