原文 : Everything You Need to Know About the CSS will-change Property

Translator: Mancuoj

Introduction to the

If you’ve ever noticed webKit-based browsers “flickering” when performing certain CSS operations (especially Transform and animation), you’ve probably come across the term “hardware acceleration.”

CPU, GPU and hardware acceleration

In short: Hardware acceleration means that graphics processing units (Gpus) help your browser render web pages and do the heavy lifting, rather than leaving it all to the CENTRAL processing unit (CPU). When a CSS operation is accelerated by hardware, its speed is also increased by faster page rendering.

As their name suggests, cpus and Gpus are processing units. The CPU is located on the computer’s motherboard, which is called the computer’s 🧠. The GPU sits on a computer’s graphics card and is responsible for processing and rendering images. In addition, gpus are specifically designed to perform complex mathematical and geometric calculations that are also necessary for graphics rendering. Thus, leaving operations to the GPU can result in huge performance gains, as well as reduced CPU contention on mobile devices.

Hardware acceleration (also known as GPU acceleration) relies on the layered model that browsers use to render pages. When something is done to an element on a page (such as a 3D transformation), the element is moved to its own “layer”, where it can be rendered independently of the rest of the page and then composited (painted onto the screen). This isolates the rendering of content, and if the transformation of that element is the only change between frames, the rest of the page does not have to be re-rendered, which often provides a significant speed advantage. A caveat: only 3D transformations have their own layers, 2D transformations don’t.

Animation, Transform, and Transition of CSS are not automatically gPU-accelerated, but are generally implemented by the browser’s slow software rendering engine. However, some browsers provide hardware acceleration through certain attributes for better rendering performance.

Opacity, for example, is one of the few CSS properties that can be properly accelerated because it can be easily manipulated by the GPU. Basically, if you desalinate the opacity in transition or animation, the browser will intelligently dump it on the GPU, very fast. Opacity is one of the most efficient CSS properties, and you won’t have any problems using it. Other common hardware acceleration operations are 3D transformations of CSS.

The Old: translateZ() (or translate3d()) Hack

We’ve been using translateZ() (or Translate3D ()) hacks (sometimes called null Transform hacks) to trick browsers into pushing animation and Transform to hardware acceleration for a long time.

This is done by adding a simple three-dimensional transformation to an element that does not transform in three-dimensional space. For example, a two-dimensional element can add this simple rule for hardware acceleration:

transform: translate3d(0.0.0);
Copy the code

Hardware-accelerated operations create a so-called Compositor layer and upload it to the GPU for compositing. However, forcing a layer doesn’t necessarily solve performance bottlenecks on some pages.

While layer creation techniques can speed up pages, they come at a cost: They will take up system RAM and memory on the GPU (limited) on the mobile device, and it has a large number of layers will produce bad influence, particularly in mobile devices, so must use them wisely and make sure it can really help page performance, and performance bottlenecks is not caused by other operations on your page.

To avoid layer creation hacks, we introduced a new CSS property will-change that allows us to tell the browser what changes we might make to an element in advance, so that the browser can optimize the element appropriately in advance. For example, some potentially substantial preparatory work is done before the animation actually begins.

The New: will-change

The will-change attribute allows you to tell the browser in advance what changes you might make to an element, so it can set appropriate optimizations ahead of time, avoiding startup costs that could negatively affect the responsiveness of a page. These elements can be changed and rendered more quickly, and pages can be updated quickly, leading to a smoother experience.

For example, when a CSS 3D transformation is applied to an element, the element and its contents may rise to a new layer, as mentioned earlier, before being composited (drawn to the screen). However, setting elements on a new layer is a relatively expensive operation, which can delay the start of the transform animation by a fraction of a second, resulting in a noticeable “flicker”.

To avoid this delay, you can notify the browser some time before the change actually happens. That way, the browser will have some time to prepare for the changes, and when they happen, the element’s layer will be ready, the transformation animation will be performed, the element will be rendered, and the page will be updated quickly.

Using will-change to alert the browser to upcoming transformations, you can add this simple rule to the elements you expect to be transformed:

will-change: transform;
Copy the code

You can also declare to the browser that you intend to change the scrolling position of an element (its position and visibility in a visible scrolling window), or change its contents, one or more CSS property values, by specifying the name of the property you wish to change. If you expect or plan to change multiple items of an element, you can provide a comma-separated list of values.

For example, if you are going to change the location of an element, you can declare to the browser:

will-change: transform, opacity;
Copy the code

Specifying exactly what you want to change makes the browser better optimized for those specific changes. This is obviously a better way to speed things up without resorting to hacks and forcing the browser to create unnecessary layers.

Does will-change affect the elements it declares in addition to rendering hints to the browser?

It may or may not depend on the properties you declare and tell the browser about. If any non-initial value of an attribute creates a stacking context on the element, specifying the attribute in will-change creates a cascading context on the element.

Note: If an element has a cascading context, we can understand that the element has a higher level on the Z-axis. In other words, the elements in the page are at a higher level and closer to the user.

For example, the clip-path and opacity properties, when their values are not initial, create a cascading context on the element being applied. Thus, using one (or both) of these attributes as will-change values creates a cascading context on the element, even before the change actually occurs. The same applies to other attributes that create a cascading context on an element.

In addition, some attributes can cause the creation of an include block for a fixed-position element. For example, a converted element creates an include block for all its positioned child elements, even those that have been set to Position: fixed. So, if an attribute causes an inclusion block, specifying it as will-change will also cause a fixed-position element to produce an inclusion block.

In general, the position and size of an element in CSS are computed relative to a rectangle, called a containment block.

As mentioned earlier, some changes to will-change cause the creation of a new composition layer. However, the GPU does not support the sub-pixel anti-aliasing that cpus do in most browsers, which sometimes leads to blurring of content (especially text).

In addition, the will-change attribute has no direct effect on the element it specifies. It is just a rendering prompt to the browser, allowing it to optimize the Settings for changes to the element. It has no direct effect on elements, other than creating a stack context and containing blocks in the above case.

Will-change Notes

Knowing what Will-change can do, you might be thinking, “Just let the browser optimize everything!” It’s a reasonable idea, who wouldn’t want all their changes optimized and ready when needed?

Although will-change is 🐂🍺, it is no different than any other kind of “power” and abusing it can cause a performance hit that can crash your page.

Like any optimization, will-change has side effects that you can’t directly detect (after all, it’s just a way of talking to the browser behind the scenes), so it can be tricky to use. Here are a few things you need to be aware of when using this attribute to ensure you get the best out of it, while avoiding the damage it can cause by abusing it.

Don’t declare too many elements

As I mentioned earlier, if you tell the browser to optimize for all attribute changes that can occur on all elements:

*,
*::before,
*::after {
	will-change: all;
}
Copy the code

Although this looks good and makes sense at first, it is actually very harmful and more operations are ineffective. Not only is the all keyword an invalid value for will-change (we’ll cover a list of valid and invalid values later in the article), but such a blanket rule doesn’t work either.

Browsers optimise everything they can (remember opacity and 3D transformations?). “So it doesn’t really change anything and it doesn’t help. In fact, doing so can take up a lot of resources on your machine and, if overused, cause your page to slow down or even crash.

In other words, keeping the browser ready for changes that may or may not happen is not a good thing. Don ‘t do it.

Give the browser plenty of time

The will-change attribute is called will because it notifies the browser of an imminent change, not an ongoing one.

With will-change, we ask the browser to make certain optimizations for the changes we declare, and to achieve this, the browser needs some time to actually make those optimizations so that when the changes occur, the optimizations can be applied without delay.

Setting will-change to an element immediately before it has changed has almost no effect. (It could actually be worse than not setting it, there could be a cost of a new layer, and the animation you’re doing didn’t qualify to do a new layer before!)

For example, if a change will occur while hovering:

.element:hover {
	will-change: transform;
	transition: transform 2s;
	transform: rotate(30deg) scale(1.5);
}
Copy the code

… Telling browsers to optimize for changes that have already occurred is useless, and doing so negates the whole concept behind will-change. Instead, find a way to predict at least a little ahead of time what will change, and set will-change at that point.

For example, if an element changes when clicked, setting will-change while the element is hovering gives the browser enough time to optimize. The time between the user hovering over the element and actually clicking on it is long enough for the browser to optimize its Settings, and since human reaction times are relatively slow, the browser will have a window of about 200ms before the change actually occurs, which is long enough for it to optimize its Settings.

.element {
	transition: transform 1s ease-out;
}
.element:hover {
	will-change: transform;
}
.element:active {
	transform: rotateY(180deg);
}
Copy the code

But what if you want the change to happen when you hover, not when you click? The above declaration would be useless, and in such cases, it is often possible to find some way to predict what will happen before the action happens.

For example, the ancestor of a hover changing element might provide sufficient preparation time:

.element {
	transition: opacity .3s linear;
}

/* Declare changes to the element when the mouse enters or hovers over its ancestor */
.ancestor:hover .element {
	will-change: opacity;
}

/* Changes when elements are hovered */
.element:hover {
	opacity:.5;
}
Copy the code

However, hovering ancestors does not always indicate that the element will definitely be interacted with, so you can do things like set will-change when the view becomes active in your application, or if the element is in the visible part of the viewport, which will increase the chance that the element will be interacted with.

Delete it after the modification is complete

Browser optimizations for upcoming changes often take up a lot of resources on the machine, and these optimizations are usually removed and returned to normal behavior as soon as possible. Will-change overrides this behavior, however, and it maintains optimizations for much longer than browsers do.

Therefore, you should always remember to remove will-change after the element changes are complete so that the browser can recover the resources taken up by the optimization.

If will-change is declared in a stylesheet, it is impossible to remove it, which is why it is recommended that you set and unset it in JavaScript. With scripts, you can declare your changes to the browser and then delete will-change by listening for when those changes have been made.

For example, as we did in the style rules in the previous section, you can listen for when an element (or its ancestor) is hovering, and then set will-change when the mouse enters. If your element is being animated, you can use the DOM event animationEnd to listen for when the animation ends, and then remove will-change when animationEnd is triggered.

// An example
// Gets the element that will be animated when clicked, for example
var el = document.getElementById('element');

// Sets the change of the element when it is hovered
el.addEventListener('mouseenter', hintBrowser);
el.addEventListener('animationEnd', removeHint);

function hintBrowser() {
	// The optimizable property to change in the keyframe of the animation
	this.style.willChange = 'transform, opacity';
}

function removeHint() {
	this.style.willChange = 'auto';
}
Copy the code

Craig Buckler has written an article about capturing CSS animation events in JavaScript, so if you’re not familiar with this, check it out. Css-tricks also has an article on controlling CSS animations and transitions, which is also worth checking out.

Use with caution in style sheets

As we saw in the previous section, will-change can be used to alert the browser to upcoming changes to an element in milliseconds. This is one of the use cases for declaring will-change in a stylesheet. Although we recommend using JavaScript to set and unset will-change, in some cases it is appropriate to set it (and keep it) in a stylesheet.

One example: Set will-change on elements that may be interacted with repeatedly by the user and should respond to the user’s interaction in a fast way. The limited number of elements means that the optimizations made by the browser won’t be overused, so it won’t hurt too much.

For example, modify the sidebar by sliding it out when the user asks for it. The following rules are appropriate:

.sidebar {
	will-change: transform;
}
Copy the code

Another example: Using will-change on constantly changing elements, such as an element that moves across the screen in response to the user’s mouse movement. In this case, just declare the value of will-change in the stylesheet, because it accurately describes that the element will change periodically/constantly, so it should remain optimized.

.annoying-element-stuck-to-the-mouse-cursor {
	will-change: left, top;
}
Copy the code

Attribute values

Will-change has four property values: auto, scroll position, contents, and

.

The

value is used to specify the name of one or more attributes that you want to change. Multiple attributes are separated by commas.

Here is an example of a valid will-change declaration with a specified attribute name.

will-change: transform;
will-change: opacity;
will-change: top, left, bottom, right;
Copy the code


: excludes the keywords will-change, None, all, auto, scrollposition, and contents, as well as some keywords that are normally excluded. So, as we mentioned at the beginning of this article, will-change: all declarations are invalid and ignored by browsers.

Auto doesn’t mean anything special, and browsers don’t set any special optimizations beyond the usual ones.

Scrollposition: As the name suggests, indicates that you want to change the scrolling position of an element at any time in the near future. This value is useful because when used the browser prepares and renders content beyond what is visible in the scrollable window on the element. Browsers typically render only the contents of a scrolling window, and the portion of content beyond that window, to balance the memory and time saved by skipping rendering and to make scrolling look nice. Using will-change: Scrollposition, it can do further rendering optimization so that longer or faster scrolling of content can be done smoothly.

Contents: The contents of this element are expected to change. Browsers typically cache renderings of elements over time because most things don’t change very often, or just their location. Browser this value will be interpreted as a signal, which reduce the cache for this element, or completely avoid the elements of the cache, because if the element’s content changes frequently, so keep the content of the cache will be useless and a waste of time, so the browser will stop buffer directly, as long as the element’s content changes, keep from rendering.

Browser support

As of August 2015, Chrome 36+, Opera 24+, and Firefox 36+ support will-change.

Safari is currently implementing will-change and Edge is “under consideration”.

Write in the last

The will-change property will help us write code that is hack-free of performance optimizations and emphasize the importance of speed and performance to our CSS operations. But if you want to wear a crown, you must bear its weight, and will-change should not be trivialized, but it also needs to be used wisely. At this point, to quote Tab Atkins Jr, will-change specification editor:

Set will-change to the properties you’ll actually change, on the elements that are actually changing. And remove it when they stop.

Thanks for reading!