Skip to content

Instantly share code, notes, and snippets.

@grorg
Last active November 30, 2018 18:52
Show Gist options
  • Save grorg/6732841 to your computer and use it in GitHub Desktop.
Save grorg/6732841 to your computer and use it in GitHub Desktop.
Why does animating left paint more often than translateX?
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".
@grorg
Copy link
Author

grorg commented Sep 17, 2015

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment