Last active
November 30, 2018 18:52
-
-
Save grorg/6732841 to your computer and use it in GitHub Desktop.
Why does animating left paint more often than translateX?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As asked by John on Twitter: https://twitter.com/johnallsopp/status/383461909640384512 | |
"wow. Animate change to left via CSS, ~500 paint/reflow events. Animate translateX - 16!" | |
Answering here because it's a long response and I'm too slack to have a | |
blog/tumble/plus/facebook/myspace. | |
NOTE: This answer is out of date. See comment below. | |
The easy answer is that changing left causes relayout, which triggers a repaint, while | |
changing transforms does not. See the CSS Transforms spec, which says "In the HTML | |
namespace, the transform property does not affect the flow of the content surrounding the | |
transformed element." | |
This means that a smart browser can notice that an animation of transform can be done | |
without repainting anything. Now, you might ask "Hey, I see things move on screen, isn't | |
that repainting?" and it would be a good question. This is because a really smart browser | |
can detect that if only the position of things change, and no layout happened, it can draw | |
the content into images ONCE, and then just move those images around. Typically the | |
drawing of HTML content is slow, but moving images around on the screen is fast. [1] | |
What's funny is that my first thought on reading John's question was "why is translateZ | |
doing 16 repaints?". | |
Generally (at least in WebKit) we try to not make things into layers unless we know they | |
are moving about. This saves memory. Nearly every element that is made into a layer | |
doubles the amount of memory it is using. That's because we've drawn it into an offscreen | |
image, but there is still the offscreen image of its parent which might now have empty | |
space where the element was (this is a huge simplification, but generally applies). | |
So, really really smart browsers can paint things normally until an animation starts, | |
detect that it won't layout during the animation, start drawing those images, and then | |
move the images around. When the animation ends, and the element is in a new location, | |
throw everything out and repaint normally again. | |
But now I'm wondering what the 16 repaint events were. I don't know the Chrome devtools, | |
but in WebKit you can compositing borders in the new Inspector. This will draw outlines | |
around the GPU layers as well as little numbers that show exactly how many times you've | |
painted. It's also cool to see all the tiles and wrapper layers that we have to create in | |
order to turn the insanely complex CSS rendering model into something sensible. | |
Before I stop rambling, there is one more un-asked question: could we detect that | |
animating left didn't really cause anything to change in layout, and then make that | |
faster? Yes, we could. But it is sometimes so difficult to do things like this, and the | |
benefit isn't always worth it. Like I say below, the better solution is MAKE EVERYTHING | |
FASTER, and then you won't have to worry. | |
[1] Hence the translateZ hack, which is slightly annoying because it was a temporary | |
decision we made that we will now have to support forever. However, "support forever" will | |
hopefully become "we made everything so fast that you didn't notice that translateZ(0) is | |
now behaving slightly differently". |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that this might be out of date now.
For one, WebKit has proposed that we do start treating everything as being in a 3d space by default. You'd have to opt in to flattening behaviour. The problem now is getting everyone to agree :)