This guide teaches you how to create high-performance CSS animations.
See Why are some animations slow? to learn the theory behind these recommendations.
Browser compatibility
All of the CSS properties that this guide recommends have good cross-browser support.
Move an element
To move an element, use the translate
or rotation
keyword values of the
transform
property.
For example to slide an item into view, use translate
.
.animate {
animation: slide-in 0.7s both;
}
@keyframes slide-in {
0% {
transform: translateY(-1000px);
}
100% {
transform: translateY(0);
}
}
Items can also be rotated, in the example below 360 degrees.
.animate {
animation: rotate 0.7s ease-in-out both;
}
@keyframes rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
Resize an element
To resize an element, use the scale
keyword value of the
transform
property.
.animate {
animation: scale 1.5s both;
}
@keyframes scale {
50% {
transform: scale(0.5);
}
100% {
transform: scale(1);
}
}
Change an element's visibility
To show or hide an element, use opacity
.
.animate {
animation: opacity 2.5s both;
}
@keyframes opacity {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Avoid properties that trigger layout or paint
Before using any CSS property for animation (other than transform
and opacity
),
determine the property's impact on the rendering pipeline.
Avoid any property that triggers layout or paint unless absolutely necessary.
Force layer creation
As explained in Why are some animations slow?, by placing elements on a new layer they can be repainted without also requiring the rest of the layout to be repainted.
Browsers will often make good decisions about which items should be placed on a new layer,
but you can manually force layer creation with the
will-change
property.
As the name suggests, this property tells the browser that this element is going to be changed in some way.
In CSS this property can be applied to any selector:
body > .sidebar {
will-change: transform;
}
However, the specification
suggests this approach should only be taken for elements that are always about to change.
If the above example was a sidebar the user could slide in and out, that might be the case.
Some items on your page may not frequently change,
and so it would be better to apply will-change
using JavaScript
at a point where it becomes likely the change will occur.
You'll need to make sure to give the browser enough time to perform the optimizations needed
and then remove the property once the changing has stopped.
If you need a way to force layer creation in one of the rare browsers that doesn't support
will-change
(most likely Internet Explorer at this point),
you can set transform: translateZ(0)
.
Debug slow or janky animations
Chrome DevTools and Firefox DevTools have lots of tools to help you figure out why your animations are slow or janky.
Check if an animation triggers layout
An animation that moves an element using something other than transform
, is likely to be slow.
In the following example, I have achieved the same visual result animating top
and left
, and using transform
.
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { top: calc(90vh - 160px); left: calc(90vw - 200px); } }
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { transform: translate(calc(90vw - 200px), calc(90vh - 160px)); } }
You can test this in the following two Glitch examples, and explore performance using DevTools.
Chrome DevTools
- Open the Performance panel.
- Record runtime performance while your animation is happening.
- Inspect the Summary tab.
If you see a nonzero value for Rendering in the Summary tab, it may mean that your animation is causing the browser to do layout work.
Firefox DevTools
In Firefox DevTools the Waterfall can help you to understand where the browser is spending time.
- Open the Performance panel.
- In the panel Start Recording Performance while your animation is happening.
- Stop the recording and inspect the Waterfall tab.
If you see entries for Recalculate Style then the browser is having to begin at the start of the rendering waterfall.
Check if an animation is dropping frames
- Open the Rendering tab of Chrome DevTools.
- Enable the FPS meter checkbox.
- Watch the values as your animation runs.
At the top of the FPS meter UI you see the label Frames. Below
that you see a value along the lines of 50% 1 (938 m) dropped of 1878
.
A high-performance animation will have a high percentage, e.g. 99%
. A
high percentage means that few frames are being dropped and the animation will look smooth.
Check if an animation triggers paint
When it comes to painting, some things are more expensive than others.
For example, anything that involves a blur (like a shadow, for example) is going to take longer to paint than drawing a red box.
In terms of CSS, however, this isn't always obvious:
background: red;
and box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5);
don't necessarily look like they have vastly different performance characteristics, but they do.
Browser DevTools can help you to identify which areas need to be repainted, and performance issues related to painting.
Chrome DevTools
- Open the Rendering tab of Chrome DevTools.
- Select Paint Flashing.
- Move the pointer around the screen.
If you see the whole screen flashing, or areas that you don't think should change highlighted then you can do some investigation.
If you need to dig into whether a particular property is causing performance issues due to painting, the paint profiler in Chrome DevTools can help.
Firefox DevTools
- Open Settings and add a Toolbox button for Toggle paint flashing.
- On the page you want to inspect, toggle the button on and move your mouse or scroll to see highlighted areas.
Conclusion
Where possible restrict animations to opacity
and transform
in order to keep animations on the compositing stage of the rendering path.
Use DevTools to check which stage of the path is being affected by your animations.
Use the paint profiler to see if any paint operations are particularly expensive. If you find anything, see if a different CSS property will give the same look and feel with better performance.
Use the will-change
property sparingly,
and only if you encounter a performance issue.