How to deliver faster animations with will-change

Much of what we do in the web performance world involves giving a helping hand to the browser, pushing it in the direction we want it to go. We have preload, to make it download resources early. We have prefetch, to hint to the browser that it might want to download resources that it will probably need later.

But away from the network, there are also ways to help animations run more smoothly. This post looks at one of them: the will-change CSS property.

Changing the layout of a web page

It’s often necessary to change the way a page looks after it has finished displaying. For example, elements may have to move in response to user input.

The way this is done can have a negative impact on performance. Frame rate can drop. The interface can become unresponsive. Visitors can begin to experience ‘jank’ – that frustrating, jerky, uneven scrolling.

How a frame gets on to the screen

To understand why, it’s worth taking a (simplified) look at how a browser displays content on the screen.

For each frame, it first has to calculate the layout for all the elements on the page. It needs to know how big they are and where they should go.

Once it has done this, it can get on to the business of painting pixels.

The final step is compositing. This is the process of organising the various layers that make up the page, so that content can be displayed to the end user.

The concept of layers will be familiar to anyone who uses Photoshop, and you can think of them as pieces of a collage. There’s always a root layer (rather like the background layer in Photoshop), and sometimes that’s all that’s needed. But very often, it takes more than one layer to build up the picture the end user sees.
Assigning content to multiple layers can be useful during animations because it can reduce the work the browser has to do.

For example, if an element on the page moves, and that element is part of the root layer, then the browser has to go through the process of recalculating the layout and repainting pixels for every frame, redrawing the root layer every time.

However, if a moving element is in its own layer, it can skip over the layout and paint stages, moving straight to compositing. This is because the browser only has to deal with pasting the moving layer to the right place on the display in the current frame.

When you’re building a website, most of the time you don’t have to worry about all this. Browsers are good at working out which elements need a layer of their own. If the browser can see that an element is going to be animated, there’s a good chance that element will be promoted to its own layer.

But sometimes the browser doesn’t know.

This is often the case when an element is animated in response to user input, such as hovering over the element or clicking a button.

Fortunately, we can let the browser know that the element should probably have a layer all to itself through the will-change CSS property. will-change can take a number of values, including scroll-position and contents, but one of the most popular values is transform, putting the browser on notice that the element may be moving around in the near future.

A simple example

Here, we’ve created a box (#a1) that rotates when the user hovers over it. In the normal course of events, it wouldn’t get moved to its own layer until the animation started. This is because, when the page is first laid out, there’s nothing to indicate that it will be animated. By adding will-change: transform, we give the browser the chance to prepare for what happens when someone hovers over the element.

@keyframes rotation {
0% { transform: rotate(0); }
25% { transform: rotate(90deg); }
50% { transform: rotate(180deg); }
75% { transform: rotate(270deg); }
100% { transform: rotate(360deg); }
}
#a1 {
width:18%;
height:6em;
line-height:6em;
text-align:center;
vertical-align:middle;
background: linear-gradient(#07a7d4, #78cce3, #07a7d4);
color:#fff;
border-radius:10%;
float:left;
margin:6px;
will-change: transform;
}

#a1:hover {
animation: rotation 0.2s 1;
}

This is useful because there is a cost to moving the element to a separate layer. If the browser waits until the user hovers over the element before it does this, there could be a noticeable delay, with slow frames towards the beginning (and end) of the animation. will-change gives the browser the chance to have everything set up and ready in advance.

If you’ve come across will-change before, you’ve almost certainly also come across the health warning associated with it. Because of the very cost we’ve just mentioned, promoting lots of elements to their own layer can use up a lot of resources. At worst, your browser could crash.

You can view the layers on your page via the Layers tab in Chrome Devtools. In the example below, it looks like we’ve overdone it and given too many elements their own layer.

Since adding lots of will-change properties to your CSS could mean creating lots of new layers you never actually need, the recommended way to use will-change is through JavaScript. This way, it can be added to an element shortly before it’s needed and removed shortly afterwards. For example, if an element animates when someone hovers over it, you could add will-change: transform when someone hovers over the containing element. This more closely mimics ‘normal’ browser behaviour – it just leaves a little extra breathing space.

While will-change can be very useful, it’s probably fair to say that it should be handled with care, since it’s possible for it to do more harm than good. Still, it’s a good idea to be aware of it, even if only to help identify those cases where it’s been overused.

Published date:  20 March 2018

Written by:  Alex Painter

comments powered by Disqus

Filter By Service

Filter By Date