Chrome がメインスレッドで変換アニメーションを実行する場合と実行しない場合があるのはなぜですか? 質問する

Chrome がメインスレッドで変換アニメーションを実行する場合と実行しない場合があるのはなぜですか? 質問する

基準は何ですか?

次の例では、 CSS をアニメーション化しておりtransform、任意の場所をクリックすると (Google Chrome 内)、アニメーションは 2 秒間のwhileループによってブロックされます。

CSStransformアニメーションがブロックされるのはなぜですか?

編集:transform最近、Chrome はメイン スレッドがブロックされている間は をブロックしなくなりました。これは、次の例のようなアニメーションがメイン スレッドから移動されたことを示しています。

アニメーション変換は別のスレッドで実行される可能性がありますが、いつ実行されるかは明確ではありません。 機能する場合もあります。

この最初の例では、別スレッドの変換アニメーションは発生しません (クリックするとメイン スレッドがブロックされ、アニメーションが一時停止します)。

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>

コードペンリンク

編集: この例のアニメーションは Safari ではブロックされていないようですが (グラデーションは切り取られます)、Chrome と Firefox でのみブロックされています。Chrome と Firefox でアニメーションのブロックを解除するにはどうすればよいですか?

次の例では、メイン スレッドをブロックするために任意の場所をクリックすると (Chrome の場合)、transformアニメーションが継続されるため別のスレッドでアニメーション化されますが、メイン スレッドではアニメーションが実行されているstroke-offsetためにアニメーションがフリーズしていることがわかります。stroke-offset

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>

最初の例のtransformアニメーションはメイン スレッドで実行されるのに、2 番目の例のtransformアニメーションは別のスレッドで実行されるのはなぜですか?

が別のスレッドで実行されることが保証される基準は何ですかtransform(少なくとも Chrome では)?

ベストアンサー1

ブラウザスレッド

各ブラウザには少なくとも 3 つのスレッドがあります。各スレッドで実行される内容はブラウザによって異なります。最新のブラウザはすべて 3 つ以上ありますが、それでも 3 つのスレッド カテゴリは常に分離されています。なぜでしょうか。1 つは完全に分離されており、スクロールや新しいタブを開くなどの処理のためにブラウザからのみアクセス可能です。少なくとも 1 つは計算や解析などの処理用であるため、CPU で実行されます。また、画面に何かを表示するために必要なため、少なくとも 1 つのスレッドは GPU で実行されます。

レイヤー

GPU が画面に表示されているものを認識するには、ビットマップ形式でラスタライズされたレイアウトが必要です。しかし、画面上で物体が動くので、GPU に移動可能なビットマップをいくつか送信するのが最善です。これらをレイヤーと呼びます。

@irdkwmnsb が指摘したように、layers開発者ツールのタブを使用して、どの要素が個別のビットマップに分割されているかを正確に確認できます。

ここに画像の説明を入力してください

レイヤーを明示的に作成する

変換されることがわかっている HTML または SVG 要素については、次の CSS ルールを追加して、要素が別のビットマップ レイヤーに分離され、メイン スレッド上の他のアクティビティによって遷移がブロックされないようにすることができます。
will-change: transform

CSSルールを追加すると

.shimmerSurfaceContent {
   will-change: transform;
}

最初の例では、遷移がブロックされないようにする必要があります。

なぜ一部のブラウザだけなのですか?

一部のブラウザがこの要素を別のレイヤーに自動的に分割しない理由は、ビットマップ レイヤーをあまり多く作成するとパフォーマンスに問題が生じるため、あまり多く作成しないように注意しているためです。また、個別のビットマップとして作成して移動すると見栄えが悪くなるものもあるため、ブラウザがそれを避ける場合があります。

しかし、この例に限って言えば、この画像の 2 つのビットマップ レイヤーを見ると、上のレイヤーのエッジが半透明になっていることがわかります。このようなことは、これまで GPU がさまざまな黄色の陰影を計算する際にエイリアシングの問題を引き起こしていました。
ここに画像の説明を入力してくださいこれが、Chrome がこれまでこれを新しいビットマップ レイヤーに分離することを避けてきた理由かもしれません。

おすすめ記事