How to use and how works CSS' will-change property? - css

I found CSS will-changeW3.org docs, MDN docs property (which already works in Chrome and is partiali supported by Firefox and Opera) but I'm not really sure how it works.
Does anybody know something more about this mysterious thing?
I have just read that it allows browser to prepare for calculation over the element in the future.
I don't want to misunderstand it. So I have a few questions.
Should I add this property to the element class or its hover state?
.my-class{
will-change: 'opacity, transform'
}
.my-class:hover{
opacity: 0.5
}
.my-class:active{
transform: rotate(5deg);
}
OR
.my-class{
...
}
.my-class:hover{
will-change: 'opacity'
opacity: 0.5
}
.my-class:active{
will-change: 'transform'
transform: rotate(5deg);
}
How can it increase a browser performance? Theoretically, when CSS is loaded, the browser already "knows" what will happen with each element, doesn't it?
If you can add any good example illustrating how to use it efficiently, I will be grateful :)

I won't copy paste the entire article here but here's a tl;dr version:
Specifying what exactly you want to change allows the browser to make better decisions about the optimizations that it needs to make for these particular changes. This is obviously a better way to achieve a speed boost without resorting to hacks and forcing the browser into layer creations that may or may not be necessary or useful.
How to use it:
will-change: transform, opacity;
How not to use it:
will-change: all;
.potato:hover {
will-change: opacity;
opacity:1;
}
Specifying will-change on hover has no effect:
Setting will-change on an element immediately before it changes has
little to no effect. (It might actually be worse than not setting it
at all. You could incur the cost of a new layer when what you’re
animating wouldn’t have previously qualified for a new layer!)

I spent some time on searching how will-change property works and how we use it. I hope, it will be useful summary. Thanks everybody for answers.
1. Layers Hack / Null Transform Hack
In 'ancient times' (like 2 years ago) somebody discovered that you can draw your CSS animation faster.
How does it work?
If you add transform: translateZ(0) to a css selector, it will force a browser to move the element with this selector to the new compositor layer. What is more, it will increase performance (in most situations, use powers of GPU instead CPU) read more here.
2. Bye Bye hacks, welcome "will-change:"
Probably, it's too early to say bye bye to Layer Hack, but this time will come shortly.
The new property will change appeared in specs of CSS and will be a great successor of layer hack.
3. Browser support
For now, it's available in Chrome and Opera and partially supported by Firefox as well.
4. How to use it properly
Don’t use will-change anywhere in your CSS until after you will
complete implementing your animations. Only then should you go back to
your CSS and apply will-change. More
This is probably the most valuable advice that you can get.
There is no point to use it straight before an action begins by e.g. adding it to the :hover state of a selector. Because browser will not have required time to prepare optimization before change occurrence. Browser will need approx. 200ms to apply optimization, so for example it is better to add will-change to a element when a parent element is on hover state. More
Example:
.parent:hover .change{
will-change: opacity;
}
.change:hover{
opacity: .5;
}
You need to use it really sparingly. If you want to optimize everything, the results will be opposite than expected ones. will-change forces browser to keep optimization turned on and reserve resources like memory for changes in the future which may never happen. It is recommended to turn off will-change afterwards, when it is not necessary anymore e.g. when the animation is finished.
You can do it easily using JavaScript document.getElementById('my_element_id').style.willChange = off;

Now with the help of CSS you can do various of animations, and sometimes this animation becomes a bottleneck for a CPU. So instead of doing this job on a CPU, it would be nice to use GPU for this task. And not so long ago people started to use a trick to do this for a 2D transformation by doing something like:
.accelerate {
-webkit-transform: translate3d(0, 0, 0);
}
This made a browser think that it is doing a 3D transformation, and because all 3D transformations are using GPU, this will offload CPU. This looks hacky (because in fact it is) and will-change property is going to change this by informing the browser to look out for changes in the future, and optimize and allocate memory accordingly.
Based on W3C draft,
The will-change CSS property … allows an author to inform the UA ahead
of time of what kinds of changes they are likely to make to an
element. This allows the UA to optimize how they handle the element
ahead of time, performing potentially-expensive work preparing for an
animation before the animation actually begins.
Good idea to use will-change is to let the browser know in advance what changes on an element can be expected. This allows the browser to make the proper optimizations in advance, which leads to quicker rendering.
This information was taken from this excellent explanation about will-change property. The article has additional example when it is nice to use it and when it is useless or even detrimental

As far as I know...
It is an alternative for translate-z:0.
I dont know about hover, but afaik its best to use it on properties that are being changed gradually by JS, changing opacity, position during scrolling etc.
This property shouldnt be overused, especially on phones, tablets, using this on too many
elements can cause performance issues.
It is encouraged to remove/turn-off this property by JS when it is no longer relevant.
So example usage would be applying that at some point of scroll, with scrollTop:400, then gradually animate opacity and at lets say scrollTop:500, disable will-change again.
Source: shoptalkshow podcast - they mention this article - https://dev.opera.com/articles/css-will-change-property/ - which is probably better source of info than my post :D

Thanks to will-change CSS property we can declare to the browser our intention to change an element’s:
contents,
scroll-position,
various tag properties like transform or opacity,
declare multiple values at once: will-change: transform, opacity, top;
or removing the special optimization, using the value auto.
This allows the browser to schedule the use of GPU/Hardware Acceleration instead of the standard CPU usage.
But we have to use it wisely. We need:
to give this to an element that will change,
assign it early early enough before the change occurs,
remove it from the element that has changed and will not be anymore.
Note from MDN web docs:
will-change is intended to be used as a last resort, in order to try to deal with existing performance problems. It should not be used to anticipate performance problems.
They also mention that if we decide to use the will-change property, we should not set it rigidly in the CSS style sheet, because it probably will cause the browser to keep the optimization in memory for much longer than it is needed... It is better to use this property directly from JavaScript (there is also an example with that).
A good additional resource to explore this topic deeper: Everything You Need to Know About the CSS will-change Property.

I'm using react-window package and there is a "will-change: transform;" property on the outer div. The package dynamically render parts of a large set of data in the visible view according to the scroll position, for example only render 10 items of a 100000 length list. Without the will-change property, the list will be blank when scroll. It shows better on slower cpu.
You can see the example here: react-window-fixed-size-list-vertical. Open the developer tool , uncheck this property in styles, slow down the cpu in Performance tab and scroll the list rapidly.list blank between renders
There is also a discussion.https://github.com/bvaughn/react-window/issues/47

The best solution for me:
transform : translate(calc(-50% + 0.5px), calc(-50% + 0.5px));
But, this solution has trouble with calc in ios safari in a fixed position can cause high battery consumption

Related

Is it a bad practice or bad for performance to specify css transition parameters for all elements?

On my website (simple multipage wordpress site), I use the same css transition type for all elements that need transition, e.g. transition: something .2s ease-in-out 0s. After writing that same rule for different elements over 50 times, I thought it might be a good idea to instead write
*, *:before, *:after {
transition: none .2s ease-in-out 0s;
}
and then only specify transition-property for each element that needs to have a transition. Note that in the wildcard declaration I specify transition-property as none, instead of the default all, which should mean that no transitions should apply to all elements.
Now my question is - is this a bad practice, or can it be bad for performance? On my website it seems to work just fine, and give adequate page speed results, but I have a pretty powerful laptop and phone, with latest version of browsers. I'm just not sure how the css engine treats that wildcard transition declaration in terms of computing.
The short answer is maybe.
Here is the long answer:
So long as you're only using transitions that happen in the paint or composite part of the render lifecycle, you should be okay. Where it starts to get dicey is if you start trying to animate things that are on the style or layout part of the render lifecycle.
Browsers have worked really hard to make sure certain properties are performant in general and have given them the ability to be GPU accelerated. The main properties we're talking about here are:
Composite Layer:
Transforms (All Types)
Opacity
Paint Layer:
Colors
You want to try and make all of your transitions only use these. If you don't you will experience significant performance degradation. It is considered best practice to only animate on these properties because of this.
So just remember, if you're going to apply a wildcard selector to transition things, make sure the properties that change across different css classes are using properties that take advantage of the composite/paint part of the render lifecycle.
Me personally: I would continue to specify transitions as needed on a class by class basis, because I wouldn't want to risk this affecting performance on something that doesn't expressly need to be animated.
Here's a really fantastic article (apologies for the medium link)

How to decide whether to use CSS property "will-change"

Recently I've dived into the topic of CSS animations and transitions.
I'm aware of the CSS property will-change which could be used to give a hint to the browser, that the element will change in the near future (which could improve the rendering performance).
There are several examples where developers are using will-change to improve the performance, e.g.:
https://www.android.com/
https://www.cube.eu/
https://www.mrsandmr.com/
But the docs clearly state, that:
Warning: will-change is intended to be used as a last resort, in order to try to deal with existing performance problems. It should not be used to anticipate performance problems.
So, what are essential considerations to make, whether to use will-change - or not?
DigitalOcean made a great post about this that I would highly recommend you check out.
https://www.digitalocean.com/community/tutorials/css-will-change
As someone else stated, will-change creates a new 'layer' in the browser, so to speak. If you have multiple animations or transitions under a specific parent element, it may be a good idea to assign will-change to that parent element while leaving the animated child elements with their standard animations/transitions.
Edit: I should point out that adding will-change in multiple places will increase your paint time as someone else mentioned, which will decrease your site load speed.

Antialiased text in Firefox after CSS rotation

so I've read a lot about the current state of rotating text and not being able to perfectly get real antialiasing to happen in all browsers. It looks like the first box in the pic in chrome, but the second, jaggedy box in firefox. I've tried the most popular fixes including -webkit-backface-visibility:hidden; -webkit-font-smoothing:antialiased; and maybe one other I can't remember.
However this is not asking the same question, but a new one I havent found anywhere. These two screenshots of the same box are both taken from Firefox. The jaggedy box on the bottom is what it looks like normally, however, when I mess with the rotation attributes with another(completely different) element on the page with the css edit console, it renders the box perfect / smoothly...
I do, however, have to continue to press up or down to change the rotation value on another element for the entire box to render antialiased perfectly, then it returns to its jaggedy normal self. I rotated the div that the content is in and put the css fixes on the same div(although I did try putting the css fixes on every element) and I didn't ever seem to get any smoothness or antialising like you see in the box above...only when I rotate another element on the page in the browser. WTF?!!?!? is there a way to do this in css or is it only something the browser is doing in realtime and cannot reproduce that smoothness in CSS yet?
EDIT: PIC for comments section
For whatever reason, it seems under some circumstances browsers "forget" to antialias text while doing complex transforms.
The fix:
Using CSS to force the browser to render the transformed element in a separately-composited "layer" is one solution:
transform: rotate(…) translate3d(0px,0px,1px);
The explanation:
Many rendering engine implementations create a GPU layer for the 3d-transformed element, and composite it onto the rest of the page when painting. This alternate rendering path can force a different text-rendering strategy, resulting in better antialiasing.
The caveat:
However, this is a bit of a hack: first, we're asking for a 3-dimensional transform we don't really want; second, forcing many unnecessary GPU layers can degrade performance, especially on mobile platforms with limited RAM.
dev.opera.com hosts a good discussion of compositing, hacks using transform3d, and the CSS3 will-change property.
Jeremy if you come back and answer this I can give the answer to you. just realized I hadn't had an answer to this so I needed to put something here.
This solution worked as in the comments above:
Jeremy:
I had another thought: it could be related to creating an opengl/webgl layer behind the scenes. If you add translate3d(0px,0px,1px) after the rotate transform, does it "fuzz out" a bit more?
Answer - Yes this works to perfectly anti-alias any text in all browsers!

CSS Transition fallback based on "smoothness"

I've a web application (HTML/CSS/JS) that "transitions" between different screens - the screen are reasonably complex - having many elements on them. Actually, it's very like http://beta.usatoday.com/ except it goes up and down as well as left to right. (And it is probably slightly less complex).
So I'm using CSS3 transitions to manage the sliding between the views. On my machine they are smooth and beautiful, and the application looks really good. However, on older machines, the experience is much less satisfying. e.g. a Core2Duo laptop with integrated graphics - around 3 years old. On this, I get really jumpy transitions, and the transitions take a really long time. They are set with a duration of 0.3s, however, on the older machines, they take 4/5 seconds.
So my questions are:
What can I do to improve the smoothness on older hardware?
If I can't, is there a way to fallback based on the hardware (or by measuring the transition actual duration) so I can just set position?
For some browser versions and mobile browsers there's a bug in webkit that makes transitions very laggy. One of the fixes that's worked for me is to add:
-webkit-transform: translate3D(0, 0, 0);
Another solution had to do with how backface-visibility was rendered during transitions. The fix for that was to add the following, even if the backface wasn't previously altered.
-webkit-backface-visibility: hidden;
Without seeing your code it's hard to say whether these will help, but give em a try (and obviously sub in the appropriate prefix for the browser).

Considerations for CSS3 Transition Performance

As part of a project that needs to support mobile devices, I have been working on mimicking the iPhone toggle control using CSS3. I have the look and feel of the element pretty much there, and am using CSS3 transitions to animate its state change.
When I have the element itself on a page with nothing else, the transition is relatively smooth on iOS. However, when I combine it with other CSS elements on a page, the result in iOS is laggy as anything. It's slightly better than a raw jQuery animation, but not much.
I've set up two test pages to demonstrate what I mean (the difference is hardly noticeable in a regular browser):
Toggle Control on its own > http://ben-major.co.uk/labs/iPhone%20UI/ios_toggle.html
Combined with other elements > http://ben-major.co.uk/labs/iPhone%20UI/
I am looking for any advice on speeding up the transition in mobile devices. What could be the factors that are slowing down its performance on the full page test?
Any advice and comments welcome.
You have to be careful with this, as it can alter the z-index of the element it's applied to, but adding:
-webkit-transform-style: preserve-3d;
To the element you're applying the transition to, can speed animation up considerably, as it forces the hardware to use hardware acceleration for the animation.
If you do encounter layout bugs, you can just switch your 2d transitions to 3d values, so:
-webkit-transform: translate(100px, 100px)
becomes:
-webkit-transform: translate3d(100px, 100px, 0px)
You can see a demo of how this helps speed things up, at http://stickmanventures.com/labs/demo/spinning-gears-Chrome-preserve-3d/#
If after applying this to the element, you see it or elements around it blink upon use, then use:
-webkit-backface-visibility: hidden;
To the element, and that should correct the problem.
These tips have helped me to produce fast, efficient CSS transitions, hope they help. :)
Chrome has recently improved the 2D transition performance, and now this trick is no longer needed. The best thing is that if removed the translate3d you'll no longer have those z-index problems! Use the test to prove. http://stickmanventures.com/labs/demo/spinning-gears-Chrome-preserve-3d/
also you can try will-change: transform; , read more about it here:
https://developer.mozilla.org/en-US/docs/Web/CSS/will-change#Browser_compatibility
I think it quite old already but for anyone who still needs tricks to improve the transition performance on mobile device, you can apply :
-webkit-transform: translateZ(0);
to the element you are animating.
This trick is according to this blog : http://chrissilich.com/blog/fix-css-animation-slow-or-choppy-in-mobile-browsers/.
I have tried and it works quite well.

Resources