Animating elements on a website can make for a more eye-catching design. But achieving smooth, lag-free animations requires thoughtful optimization, especially when complex interactions and high user demands are involved.
In this article, we'll review a few actionable methods to improve animation performance using JavaScript and CSS. This is particularly helpful if you are already dealing with complex animations and are starting to see those precious frames drop.
Use CSS Transforms Instead of Position Properties
When creating animations, it’s tempting to animate properties like left, top, width, and height, in order to fade them in or to bring them into view. However, these properties require the browser to recalculate layout and re-render the page every single time.
And while not a huge deal when working with a few animatable elements, eventually things will start to degrade in terms of performance.
Instead, you can use the following:
Use transform and opacity. Transform-based animations can be handled by the GPU, which is much more efficient for smooth rendering.
For example, animate an element’s position using translateX and translateY rather than left or top if you are looking to fade an element into view.
/* Good */
transform: translateX(100px);
/* Avoid */
left: 100px;
Let's take the following as an example. Assume that you have 2 buttons and one animatable element on the screen, and depending on the button click you will get a different animation implementation.
<head>
<style>
.square {
width: 50px;
height: 50px;
background-color: #4CAF50;
position: relative;
margin-top: 20px;
}
</style>
</head>
<body>
<button id="animate-left">Animate with Left (Less Efficient)</button>
<button id="animate-transform">Animate with Transform (More Efficient)</button>
<div class="square" id="square"></div>
</body>
And here's the script with an example of each use case:
// script.js
const square = document.getElementById("square");
// Animate with the `left` property (Less Efficient)
document.getElementById("animate-left").addEventListener("click", () => {
square.style.left = "0"; // Reset position
square.style.position = "absolute"; // Needed for left to work
let leftPosition = 0;
function animateLeft() {
if (leftPosition < 300) { // Move to 300px
leftPosition += 5;
square.style.left = `${leftPosition}px`;
requestAnimationFrame(animateLeft);
}
}
animateLeft();
});
// Animate with `transform: translateX` (More Efficient)
document.getElementById("animate-transform").addEventListener("click", () => {
square.style.left = "0"; // Reset any previous position changes
let translateXValue = 0;
function animateTransform() {
if (translateXValue < 300) { // Move to 300px
translateXValue += 5;
square.style.transform = `translateX(${translateXValue}px)`;
requestAnimationFrame(animateTransform);
}
}
animateTransform();
});
Both versions essentially do the same thing, they change the X axis of the given element. But how they do it is completely different.
In the first example we're updating the 'left' CSS property of the element by 5 pixels at a time.
And in the second example, we're updating the translateX value of the transform property by the same.
And here's a quick example of that in action. If you click on "Animation 1", you will see an element transition in using plain old CSS positioning techniques.
If you click on "Animation 2" you will see an element transition in using a CSS transformation.
Animate with Left (Less Efficient)
Animation with Transform (More Efficient)
Visually, both animations might look almost identical. But under the hood they are leveraging different rendering mechanisms.
Note that this is a very simple example and it probably won't really matter which version you use for this particular use case. But on more advanced projects with many different elements rendering on the page at once, you might be able to save a few pixels and a few headaches by choosing the right approach.
Leverage hardware acceleration with will-change
CSS has a specific property named will-change that is essentially designed for improving animation performance. And combining it with CSS transformations can be the key to rendering smooth animations.
The property looks something like the following:
.element{
will-change: transform, opacity;
}
The will-change property informs the browser about which elements are likely to change in the near future. By setting this in advance, the browser can begin to make optimizations that can lead to smoother transitions and animations. This property can be applied to a few CSS features such as transform, opacity, and top.
However, this extra overhead isn't free, and overusing this property can lead to even worse performance if you end up overusing it.
You can read my full breakdown of the will-change property on the following article:
How to use the will-change CSS property to improve animations
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.