7

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.

enter image description here

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:

enter image description here

But the section using requestAnimationFrame does:

enter image description here

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().

Andrew Regan
  • 5,087
  • 6
  • 37
  • 73
  • Your js animation changes the inline style so Chrome had to recalculate styles and it probably doesn't/can't cache inline styles. Try changing a class or any other attribute instead. – wOxxOm Sep 15 '19 at 04:44
  • @wOxxOm I'm not sure what you're getting at with "cache inline styles". Are you saying that Chrome would need to cache the inline styles, so that when it changes, it can check to see what actually changed, to determine if the style needed to be recalculated? Changing class also triggered style recalculation. Changing the opacity instead of transform also triggered style recalculation. – AdenFlorian Sep 15 '19 at 17:17
  • My reasoning is dead simple. Since startCssAnimation only changes the className then this is how you should do it. Declare your animations via classes and change them accordingly. As to why, I guessed this is how Chrome implements it, which may be a bug or an intended implementation trade-off. – wOxxOm Sep 15 '19 at 17:34
  • I haven't found an answer to the exact question yet, but I did learn about the animation worklet, which looks like it will provide a possible solution to my higher level problem when it becomes available. I still think there should be a way to transform something without updating the layer tree at the very least, without using worklets. – AdenFlorian Sep 17 '19 at 02:38
  • What's wrong with using the same approach as in startCssAnimation? – wOxxOm Sep 17 '19 at 04:15
  • Because I want the position to be controlled by user input. – AdenFlorian Sep 17 '19 at 23:29
  • Maybe you can use [paint API](https://developers.google.com/web/updates/2018/01/paintapi)? It's already shipped. – wOxxOm Sep 20 '19 at 06:17

0 Answers0