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>
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)?
transform
runs on a separate thread, not the UI thread. For proof, try clicking on the following example, and only one spinner will freeze (in all browsers): codepen.io/trusktr/pen/mgPOEK The question is, why does my example freeze, if I'm using transform? – Sectortransform
in in fact running on a separate thread. – Sectortransform
s are only sometimes blocked when JS is stuck in a loop. In my last comment's example, click to run a synchronous JS loop, and you will see that only one of the two animations is frozen, showing thattransform
runs in a separate thread in that case. The question is, why is that true in that particular example, whiletransform
can be blocked in other cases. (The OP example's transform is lately not blocked in Chrome.) – Sector