0

I found chrome will do a huge Recalculate Style and Pre-Paint for existing animations when I start a new animation wherever on the page. Is this by design or a Chromium bug? Imagine my real web page has thousands of DOM nodes and hundreds of existing animations, doing such animation invalidation will cause a huge CPU spike. Any ways I can avoid this?

PS:

  1. normally chrome devtools won't show invalidation detail, go to devtools settings > Experiments > check Performance panel: invalidation tracking, and there you go.
  2. I deliberately throttled cpu 20 times down to clearly show the problem cause the repro has much less DOM nodes than real scenario. enter image description here

The reproducible code is pretty simple. I defined five floating elements running animation when the page starts. I also defined five backup elements which have content and classNames already. Then click the button to periodically start a new animation. To not introduce other factors that way cause reflow or relayout, I'm using Web Animation API instead of toggling className to start a new animation.

const keyframes = [{
    transform: "initial",
    willChange: "transform"
  },
  {
    transform: "translateX(0)",
    offset: 0.01,
    willChange: "transform"
  },
  {
    transform: "translateX(-100vw)",
    willChange: "initial"
  }
];
const timing = {
  duration: 3000,
  iterations: Infinity,
  easing: "linear"
};

const startButton = document.querySelector('#start');
startButton.onclick = start;

function start() {
  const backupList = Array.from(document.querySelectorAll(".backup"));
  let index = 0;
  setInterval(() => {
    if (index >= backupList.length) return;
    const div = backupList[index];
    div.animate(keyframes, timing);
    index++;
  }, 1000);
}
.container {
  width: 100vw;
  height: 100px;
  background-color: red;
  position: relative;
}

.floating,
.backup {
  position: absolute;
  will-change: transform;
  color: black;
  top: 0;
  left: 100vw;
}

.floating {
  animation: float linear 3s infinite;
}

@keyframes float {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100vw);
  }
}
<div class="container">
  <div class="floating">floating</div>
  <div class="floating">floating</div>
  <div class="floating">floating</div>
  <div class="floating">floating</div>
  <div class="floating">floating</div>
  <div class="backup">backup</div>
  <div class="backup">backup</div>
  <div class="backup">backup</div>
  <div class="backup">backup</div>
  <div class="backup">backup</div>
</div>
<button id="start">Start</button>

0