Problem/Question:
A CSS animation is able to smoothly change the transform of an element without triggering Recalculate Style
or Update Layer Tree
.
Why can't we do the same from JavaScript, so that we can smoothly change the transform in response to something like when the user moves their mouse?
Demo: https://jsfiddle.net/7frvyoqx/5/
const testElement = document.getElementById('test')
const interval = 2000
startCssAnimation()
function startCssAnimation() {
testElement.className = 'animate'
setTimeout(() => {
startJsAnimation()
}, interval)
}
function startJsAnimation() {
testElement.className = ''
setTimeout(() => {
startCssAnimation()
jsAnimateGo = false
}, interval)
jsAnimateGo = true
frame = 0
requestAnimationFrame(jsAnimate)
}
let frame = 0
let jsAnimateGo = false
function jsAnimate() {
if (jsAnimateGo) {
testElement.style.transform = `translateX(${frame++}px)`
requestAnimationFrame(jsAnimate)
}
}
#test {
background-color: green;
transform: translateX(0);
}
.animate {
animation-name: move;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(200px);
}
}
<div id="test">hello</div>
The demo alternates between animating a div's transform
property with a CSS animation and a requestAnimationFrame
in JavaScript.
If you open the chrome dev tools to the Performance tab, and record a session for 10 seconds, then you can see some clear differences between the 2 methods.
If you zoom into each of the 2 big sections, you'll see that the section with the CSS animation does not have any "Recalculate Styles" events:
But the section using requestAnimationFrame does:
I would expect that the styles would not need to be recalculated when the same CSS property is changed to the same values as it is from the CSS animation, because it doesn't get recalculated when the CSS animation does it.
I'm aware that some part of the CSS animation is done in another thread, but that doesn't mean that the style is being recalculated in another thread, does it?
I'm hoping this is either a performance regression in Chrome, or an enhancement that could be added, or that there's some trick to this that I haven't found yet.
If it's none of the above, I'm looking for an answer as to why it can't be done, or why it's necessary to recalculate styles when using js but not when using CSS animations.
Windows 10
Chrome Version 77.0.3865.75 (Official Build) (64-bit)
Same results when using setInterval
instead of RAF.
Same results in 2 past versions of chrome: Version 75.0.3770.0 (Developer Build) (64-bit) Version 76.0.3809.0 (Developer Build) (64-bit)
Same results when using a custom CSS property and changing the value with testElement.style.setProperty()
.