为什么从 JavaScript 更改转换会导致重新计算样式事件,而 CSS 动画不会?

Ade*_*ian 6 javascript blink google-chrome-extension google-chrome-devtools css-animations

问题/疑问:

CSS 动画能够在不触发Recalculate Style或 的情况下平滑地改变元素的变换Update Layer Tree

为什么我们不能从 JavaScript 中做同样的事情,以便我们可以平滑地更改转换以响应诸如用户移动鼠标之类的事情?

演示: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)
  }
}
Run Code Online (Sandbox Code Playgroud)
#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);
  }
}
Run Code Online (Sandbox Code Playgroud)
<div id="test">hello</div>
Run Code Online (Sandbox Code Playgroud)

该演示在transform使用 CSS 动画和requestAnimationFrameJavaScript为 div属性设置动画之间交替进行。

如果您打开 chrome 开发工具到 Performance 选项卡,并记录 10 秒的会话,那么您可以看到两种方法之间的一些明显差异。

在此处输入图片说明

如果放大 2 个大部分中的每一个,您会看到带有 CSS 动画的部分没有任何“重新计算样式”事件:

在此处输入图片说明

但是使用 requestAnimationFrame 的部分确实:

在此处输入图片说明

我希望当相同的 CSS 属性更改为与 CSS 动画相同的值时不需要重新计算样式,因为在 CSS 动画执行时不会重新计算它。

我知道 CSS 动画的某些部分是在另一个线程中完成的,但这并不意味着正在另一个线程中重新计算样式,是吗?

我希望这要么是 Chrome 中的性能回归,要么是可以添加的增强功能,或者有一些我还没有找到的技巧。

如果以上都不是,我正在寻找关于为什么不能完成的答案,或者为什么在使用 js 时需要重新计算样式而不是在使用 CSS 动画时重新计算样式。

Windows 10
Chrome 版本 77.0.3865.75(官方版本)(64 位)

使用setInterval代替 RAF时的结果相同。

在过去的 2 个 chrome 版本中的结果相同: 版本 75.0.3770.0(开发人员版本)(64 位)版本 76.0.3809.0(开发人员版本)(64 位)

使用自定义 CSS 属性并使用testElement.style.setProperty().