javascript - Why does Chrome run transform animation on the main thread in some cases, and not in others? - Stack Overflow

What is the criteria?In the following example, I am animating CSS transform, and when you click anywhe

What is the criteria?

In the following example, I am animating CSS transform, and when you click anywhere (while in Google Chrome) the animation is blocked by a 2-second-long while loop.

Why is the CSS transform animation blocked?

EDIT: Lately Chrome no longer blocks the transform while the main thread is blocked, indicating that they have moved the sort of animation in the following example off main thread.

Animating transform can happen on a separate thread, but it isn't clear exactly when. Sometimes it works.

In this first example, separate-thread transform animation does not happen (click on it to block the main thread and therefore pause the animation):

window.addEventListener('click', kill)

function kill() {
  var start = +new Date;
  while (+new Date - start < 2000){}
}
html, body, div {
        width: 100%; height: 100%;
        margin: 0; padding: 0;
        /* background: #364659; */
        /* background: #293442; */
        background: #1E2630;
        overflow: hidden;
    }

    @keyframes ShimmerEffect {
        0% { transform: translate3d(-15%, -15%, 0) }
        100% { transform: translate3d(-60%, -60%, 0) }
    }
    .shimmerSurface {
/*         overflow: hidden; */
/*         perspective: 100000px */
    }
    .shimmerSurfaceContent {
        transform-style: preserve-3d;
        background: linear-gradient(
            -45deg,
            rgba(0,0,0,0) 40%,
            rgba(244,196,48,0.6) 50%,
            rgba(0,0,0,0) 60%
        );
        background-repeat: repeat;
        background-size: 100% 100%;
        width: 400%; height: 400%;

        animation: ShimmerEffect 1.8s cubic-bezier(0.75, 0.000, 0.25, 1.000) infinite;
    }
<div class="shimmerSurface">
    <div class="shimmerSurfaceContent"></div>
</div>

What is the criteria?

In the following example, I am animating CSS transform, and when you click anywhere (while in Google Chrome) the animation is blocked by a 2-second-long while loop.

Why is the CSS transform animation blocked?

EDIT: Lately Chrome no longer blocks the transform while the main thread is blocked, indicating that they have moved the sort of animation in the following example off main thread.

Animating transform can happen on a separate thread, but it isn't clear exactly when. Sometimes it works.

In this first example, separate-thread transform animation does not happen (click on it to block the main thread and therefore pause the animation):

window.addEventListener('click', kill)

function kill() {
  var start = +new Date;
  while (+new Date - start < 2000){}
}
html, body, div {
        width: 100%; height: 100%;
        margin: 0; padding: 0;
        /* background: #364659; */
        /* background: #293442; */
        background: #1E2630;
        overflow: hidden;
    }

    @keyframes ShimmerEffect {
        0% { transform: translate3d(-15%, -15%, 0) }
        100% { transform: translate3d(-60%, -60%, 0) }
    }
    .shimmerSurface {
/*         overflow: hidden; */
/*         perspective: 100000px */
    }
    .shimmerSurfaceContent {
        transform-style: preserve-3d;
        background: linear-gradient(
            -45deg,
            rgba(0,0,0,0) 40%,
            rgba(244,196,48,0.6) 50%,
            rgba(0,0,0,0) 60%
        );
        background-repeat: repeat;
        background-size: 100% 100%;
        width: 400%; height: 400%;

        animation: ShimmerEffect 1.8s cubic-bezier(0.75, 0.000, 0.25, 1.000) infinite;
    }
<div class="shimmerSurface">
    <div class="shimmerSurfaceContent"></div>
</div>

(codepen link)

EDIT: seems the example's animation is not blocked in Safari (though it chops the gradient), but is blocked only in Chrome and Firefox. How can we unblock the animation in Chrome and Firefox?

In next example, when you click anywhere to block the main thread (in Chrome), you will see that transform is animated on a separate thread because it continues to animate, while the stroke-offset animation is frozen because apparently stroke-offset animation is happening on the main thread:

window.addEventListener('click', kill)

function kill() {
  var start = +new Date;
  while (+new Date - start < 2000){}
}
.loader {
  --path: #2F3545;
  --dot: #5628EE;
  --duration: 3s;
  width: 44px;
  height: 44px;
  position: relative;
}
.loader:before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  position: absolute;
  display: block;
  background: var(--dot);
  top: 37px;
  left: 19px;
  transform: translate(-18px, -18px);
  -webkit-animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
          animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg {
  display: block;
  width: 100%;
  height: 100%;
}
.loader svg rect,
.loader svg polygon,
.loader svg circle {
  fill: none;
  stroke: var(--path);
  stroke-width: 10px;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.loader svg polygon {
  stroke-dasharray: 145 76 145 76;
  stroke-dashoffset: 0;
  -webkit-animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
          animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg rect {
  stroke-dasharray: 192 64 192 64;
  stroke-dashoffset: 0;
  -webkit-animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
          animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg circle {
  stroke-dasharray: 150 50 150 50;
  stroke-dashoffset: 75;
  -webkit-animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
          animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader.triangle {
  width: 48px;
}
.loader.triangle:before {
  left: 21px;
  transform: translate(-10px, -18px);
  -webkit-animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
          animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}

@-webkit-keyframes pathTriangle {
  33% {
    stroke-dashoffset: 74;
  }
  66% {
    stroke-dashoffset: 147;
  }
  100% {
    stroke-dashoffset: 221;
  }
}

@keyframes pathTriangle {
  33% {
    stroke-dashoffset: 74;
  }
  66% {
    stroke-dashoffset: 147;
  }
  100% {
    stroke-dashoffset: 221;
  }
}
@-webkit-keyframes dotTriangle {
  33% {
    transform: translate(0, 0);
  }
  66% {
    transform: translate(10px, -18px);
  }
  100% {
    transform: translate(-10px, -18px);
  }
}
@keyframes dotTriangle {
  33% {
    transform: translate(0, 0);
  }
  66% {
    transform: translate(10px, -18px);
  }
  100% {
    transform: translate(-10px, -18px);
  }
}
@-webkit-keyframes pathRect {
  25% {
    stroke-dashoffset: 64;
  }
  50% {
    stroke-dashoffset: 128;
  }
  75% {
    stroke-dashoffset: 192;
  }
  100% {
    stroke-dashoffset: 256;
  }
}
@keyframes pathRect {
  25% {
    stroke-dashoffset: 64;
  }
  50% {
    stroke-dashoffset: 128;
  }
  75% {
    stroke-dashoffset: 192;
  }
  100% {
    stroke-dashoffset: 256;
  }
}
@-webkit-keyframes dotRect {
  25% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(18px, -18px);
  }
  75% {
    transform: translate(0, -36px);
  }
  100% {
    transform: translate(-18px, -18px);
  }
}
@keyframes dotRect {
  25% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(18px, -18px);
  }
  75% {
    transform: translate(0, -36px);
  }
  100% {
    transform: translate(-18px, -18px);
  }
}
@-webkit-keyframes pathCircle {
  25% {
    stroke-dashoffset: 125;
  }
  50% {
    stroke-dashoffset: 175;
  }
  75% {
    stroke-dashoffset: 225;
  }
  100% {
    stroke-dashoffset: 275;
  }
}
@keyframes pathCircle {
  25% {
    stroke-dashoffset: 125;
  }
  50% {
    stroke-dashoffset: 175;
  }
  75% {
    stroke-dashoffset: 225;
  }
  100% {
    stroke-dashoffset: 275;
  }
}
.loader {
  display: inline-block;
  margin: 0 16px;
}

html {
  -webkit-font-smoothing: antialiased;
}

* {
  box-sizing: border-box;
}
*:before, *:after {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  background: #F5F9FF;
  display: flex;
  justify-content: center;
  align-items: center;
}
body .dribbble {
  position: fixed;
  display: block;
  right: 20px;
  bottom: 20px;
}
body .dribbble img {
  display: block;
  height: 28px;
}
<div class="loader">
    <svg viewBox="0 0 80 80">
        <circle id="test" cx="40" cy="40" r="32"></circle>
    </svg>
</div>

<div class="loader triangle">
    <svg viewBox="0 0 86 80">
        <polygon points="43 8 79 72 7 72"></polygon>
    </svg>
</div>

<div class="loader">
    <svg viewBox="0 0 80 80">
        <rect x="8" y="8" width="64" height="64"></rect>
    </svg>
</div>

Why does the first example's transform animation run on the main thread, while the second example's transform animation runs on a separate thread?

What are the criteria under which a transform is guaranteed to run in a separate thread (at least, in Chrome)?

Share Improve this question edited Dec 14, 2021 at 7:08 trusktr asked Apr 4, 2019 at 4:14 trusktrtrusktr 45.6k58 gold badges210 silver badges287 bronze badges 12
  • 2 I believe things are opposite: CSS animation does not block main thread. but can be blocked by main thread. – skyboyer Commented Apr 4, 2019 at 4:54
  • 1 @MasoudKeshavarz Yes, the main thread freezes, but in Safari the animation continues running (on another CPU thread) even while the JS is frozen. It only does not work in Chrome or Firefox (although I read a bunch of other places that it should work). – trusktr Commented Apr 4, 2019 at 16:08
  • 2 In short, that is because CSS animations take place on the UI thread. On desktop/iOS Safari and Android Chrome, these browsers have since moved CSS animations (positing) away from using the UI thread, which means that they will not be affected by the 2-second infinity loop invoked by the click event callback. – Terry Commented Apr 6, 2019 at 20:38
  • 2 At the time of this ment your example is no longer blocking animation when clicking the page. This makes the question unanswerable. Voting to close it, since the issue appears to have been fixed. – tao Commented Jan 7, 2022 at 23:26
  • 1 To better debug transform issues see "Layers" tab in Devtools -> [three dots in the upper right corner] -> More tools -> Layers – irdkwmnsb Commented Apr 25, 2022 at 13:20
 |  Show 7 more ments

1 Answer 1

Reset to default 3

Browser Threads

Each browser has at least three threads; precisely what is run on each depends on the browser. Modern browsers all have more than three now, but they still have three categories of threads that will always be separate. Why? One will always be entirely separate and only accessible by the browser to handle things like scrolling, opening a new tab etc... At least one will always be for things like calculating and parsing and so will be run on the CPU. And at least one thread will run on the GPU as it is required for something to be shown on your screen.

Layers

For the GPU to know what it's showing on the screen it needs the layout rasterised in a bitmap format. But as things move around the screen it's best if we send the GPU a few bitmaps that can move around. We call these layers.

as @irdkwmnsb has pointed out we can use the layers tab in the developer tools to see exactly which elements have been split into separate bitmaps.

Explicitly Creating A Layer

For any HTML or SVG element that we know will transform, we can add the following CSS rule to ensure the element is separated into a separate bitmap layer and the transition shouldn't be blocked by other activity on the main thread:
will-change: transform

so adding the CSS rule

.shimmerSurfaceContent {
   will-change: transform;
}

should stop the transition from being blocked in your first example.

Why Only In Some Browsers?

The reason some browsers may not automatically split this element into a separate layer is that there is a performance issue with creating too many bitmap layers so they are careful not to create too many. Also, some things don't look good when created as separate bitmaps and moved around so the browser may avoid it.

But for this example specifically, we can see from the two bitmap layers in this image that the top one has a semi-transparent edge. Things like this have previously caused aliasing problems for the GPU as it calculates the various shaded of yellow.
This may have been a reason for chrome to previously avoid separating it into a new bitmap layer.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744265800a4565858.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信