CSS 动画不同步 1 帧
CSS animations out of sync by 1 frame
我有两个名为 slide
和 bounce-return
的动画,它们都应该以 0 延迟和 1 秒持续时间播放。由于我怀疑是动画处理逻辑中的某种竞争条件,无论动画速度或刷新率如何,这些动画都不同步 1 帧。我怎样才能阻止这些圈子跳进跳出?
(如果您没有立即发现任何问题,请在整页中查看代码段或打开开发者工具。)
.items {
--var-circle-size: 16px;
--var-circle-space: 8px;
--var-circle-border: solid red;
--var-circle-border-width: 0px;
--var-circle-shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--var-circle-shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--var-circle-count: 5;
--var-anim-bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--var-anim-time-unit: calc(1s);
--var-anim-color-cycle-time: calc(var(--var-circle-count)*var(--var-anim-time-unit));
--var-anim-color-cycle-func: steps(1);
--var-anim-color-1: #6db;
--var-anim-color-2: #be4;
--var-anim-color-3: #ec5;
--var-anim-color-4: #d86;
--var-anim-color-5: #f8c;
--var-anim-slide-time: var(--var-anim-time-unit);
--var-anim-slide-func: linear;
--var-circle-size-space: calc(var(--var-circle-size) + var(--var-circle-space));
--var-anim-slide-amount: calc(var(--var-circle-size-space)/2);
--var-anim-slide-start: calc(-1*var(--var-anim-slide-amount));
--var-anim-slide-end: var(--var-anim-slide-amount);
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
margin-right: calc(-1*var(--var-circle-space) - var(--var-circle-size-space));
animation: slide var(--var-anim-slide-time) var(--var-anim-slide-func) infinite;
}
.circle {
width: var(--var-circle-size);
height: var(--var-circle-size);
border-radius: 100%;
box-shadow: var(--var-circle-shadow);
display: inline-block;
margin: 0;
margin-right: var(--var-circle-space);
box-sizing: border-box;
border: var(--var-circle-border);
border-width: min(var(--var-circle-border-width), calc(var(--var-circle-size)/2));
background-color: var(--var-anim-bgcolor);
--var-color-cycle-anim: anim-color-cycle var(--var-anim-color-cycle-time) var(--var-anim-color-cycle-func) calc(var(--var-anim-delay-fact)*var(--var-anim-time-unit)) infinite;
animation: var(--var-color-cycle-anim);
}
@keyframes anim-color-cycle {
0% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
20% {
--var-anim-bgcolor: var(--var-anim-color-2);
}
40% {
--var-anim-bgcolor: var(--var-anim-color-3);
}
60% {
--var-anim-bgcolor: var(--var-anim-color-4);
}
80% {
--var-anim-bgcolor: var(--var-anim-color-5);
}
100% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
}
@keyframes slide {
0% {
transform: translate(var(--var-anim-slide-start), 0);
}
100% {
transform: translate(var(--var-anim-slide-end), 0);
}
}
@keyframes bounce-inout {
40%,
60% {
transform: translate(0, var(--var-anim-bounce-inout-fact));
box-shadow: var(--var-circle-shadow-high);
}
}
@keyframes bounce-return {
10% {
left: 0;
}
90%,
100% {
left: var(--var-anim-bounce-return-amount);
}
}
@keyframes bounce-inout-fact-alternate {
0% {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
}
50% {
--var-anim-bounce-inout-fact: var(--var-circle-size-space);
}
}
.circle:nth-child(1) {
--var-anim-delay-fact: -4;
}
.circle:nth-child(2) {
--var-anim-delay-fact: -3;
}
.circle:nth-child(3) {
--var-anim-delay-fact: -2;
}
.circle:nth-child(4) {
--var-anim-delay-fact: -1;
}
.circle:nth-child(5) {
--var-anim-delay-fact: -0;
}
.circle:last-child {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
--var-anim-bounce-return-amount: calc(-1 * var(--var-circle-count) * var(--var-circle-size-space));
position: relative;
animation: var(--var-color-cycle-anim), bounce-inout var(--var-anim-time-unit) var(--var-anim-bounce-func) infinite, bounce-return var(--var-anim-time-unit) ease-in-out infinite, bounce-inout-fact-alternate calc(2*var(--var-anim-time-unit)) steps(1) infinite;
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
可能的解决方法:添加只有 color-cycle
且没有移动动画的圆圈的复制版本,并让它们与原始圆圈重叠,并在动画迭代即将重新开始时将其可见性交换几毫秒.
我有一个理论,将所有内容组合到每个圆圈的一组关键帧中,而不是尝试将项目上的动画与圆圈动画同步,这可能有助于解决相位调整问题。
为了测试它,这里有一个片段可以粗略地重现给定代码的操作。其中的项目 div 没有动画。每个圆圈都保持其原始颜色,并向右、向上、向后、向右、向下、向后动画 - 两端几乎没有弹跳。
未观察到 'flashing/shadowing'。完成的处理量也更少。
关于问题中的代码,我的笔记本电脑显示平均 5% cpu 和 15% 的 gpu 使用率。在此片段中,它们分别约为 1.5% 和 11.5%。
.items {
--size: 16px;
--space: 8px;
--border: solid red;
--border-width: 0px;
--shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--count: 5;
--bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--time-unit: calc(1s);
--cycle-time: calc(2 * var(--count)*var(--time-unit));
--color-cycle-func: steps(1);
--color-1: #6db;
--color-2: #be4;
--color-3: #ec5;
--color-4: #d86;
--color-5: #f8c;
--slide-time: var(--time-unit);
--slide-func: linear;
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
width: calc((var(--size) + var(--space)) * var(--count));
height: var(--size);
position: relative;
}
.circle {
position: absolute;
width: var(--size);
height: var(--size);
border-radius: 100%;
box-shadow: var(--shadow);
display: inline-block;
margin: 0;
margin-right: var(--space);
box-sizing: border-box;
border: var(--border);
border-width: min(var(--border-width), calc(var(--size)/2));
animation: bounceround infinite var(--cycle-time) var(--slide-func);
animation-delay: var(--delay);
background-color: var(--color);
}
.circle:nth-child(1) {
--color: var(--color-1);
--delay: -8s;
}
.circle:nth-child(2) {
--color: var(--color-2);
--delay: -6s;
}
.circle:nth-child(3) {
--color: var(--color-3);
--delay: -4s;
}
.circle:nth-child(4) {
--color: var(--color-4);
--delay: -2s;
}
.circle:nth-child(5) {
--color: var(--color-5);
--delay: 0s;
}
@keyframes bounceround {
0% {
}
36.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
40% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(-100% - var(--size))); /* go up a bit */
}
41% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(-100% - (var(--size) / 2))); /* come down a fraction */
}
45% {
transform: translateX(0) translateY(-100%); /* move to the left */
}
49% {
transform: translateX(0) translateY(calc(var(--size) / 4)); /* jump down a bit*/
}
50% {
transform: translateX(0) translateY(0); /* go down to start position */
}
86.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
90% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(100% + var(--size))); /* go down a bit */
}
91% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(100% + calc(var(--size) / 2))); /* go up a fraction */
}
95% {
transform: translateX(0) translateY(100%); /* move to the left */
}
99% {
transform: translateX(0) translateY(calc(-1 * var(--size) / 4)); /* jump up a bit */
}
100% {
transform: translateX(0) translateY(0); /* back to where we started */
}
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
我有两个名为 slide
和 bounce-return
的动画,它们都应该以 0 延迟和 1 秒持续时间播放。由于我怀疑是动画处理逻辑中的某种竞争条件,无论动画速度或刷新率如何,这些动画都不同步 1 帧。我怎样才能阻止这些圈子跳进跳出?
(如果您没有立即发现任何问题,请在整页中查看代码段或打开开发者工具。)
.items {
--var-circle-size: 16px;
--var-circle-space: 8px;
--var-circle-border: solid red;
--var-circle-border-width: 0px;
--var-circle-shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--var-circle-shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--var-circle-count: 5;
--var-anim-bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--var-anim-time-unit: calc(1s);
--var-anim-color-cycle-time: calc(var(--var-circle-count)*var(--var-anim-time-unit));
--var-anim-color-cycle-func: steps(1);
--var-anim-color-1: #6db;
--var-anim-color-2: #be4;
--var-anim-color-3: #ec5;
--var-anim-color-4: #d86;
--var-anim-color-5: #f8c;
--var-anim-slide-time: var(--var-anim-time-unit);
--var-anim-slide-func: linear;
--var-circle-size-space: calc(var(--var-circle-size) + var(--var-circle-space));
--var-anim-slide-amount: calc(var(--var-circle-size-space)/2);
--var-anim-slide-start: calc(-1*var(--var-anim-slide-amount));
--var-anim-slide-end: var(--var-anim-slide-amount);
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
margin-right: calc(-1*var(--var-circle-space) - var(--var-circle-size-space));
animation: slide var(--var-anim-slide-time) var(--var-anim-slide-func) infinite;
}
.circle {
width: var(--var-circle-size);
height: var(--var-circle-size);
border-radius: 100%;
box-shadow: var(--var-circle-shadow);
display: inline-block;
margin: 0;
margin-right: var(--var-circle-space);
box-sizing: border-box;
border: var(--var-circle-border);
border-width: min(var(--var-circle-border-width), calc(var(--var-circle-size)/2));
background-color: var(--var-anim-bgcolor);
--var-color-cycle-anim: anim-color-cycle var(--var-anim-color-cycle-time) var(--var-anim-color-cycle-func) calc(var(--var-anim-delay-fact)*var(--var-anim-time-unit)) infinite;
animation: var(--var-color-cycle-anim);
}
@keyframes anim-color-cycle {
0% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
20% {
--var-anim-bgcolor: var(--var-anim-color-2);
}
40% {
--var-anim-bgcolor: var(--var-anim-color-3);
}
60% {
--var-anim-bgcolor: var(--var-anim-color-4);
}
80% {
--var-anim-bgcolor: var(--var-anim-color-5);
}
100% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
}
@keyframes slide {
0% {
transform: translate(var(--var-anim-slide-start), 0);
}
100% {
transform: translate(var(--var-anim-slide-end), 0);
}
}
@keyframes bounce-inout {
40%,
60% {
transform: translate(0, var(--var-anim-bounce-inout-fact));
box-shadow: var(--var-circle-shadow-high);
}
}
@keyframes bounce-return {
10% {
left: 0;
}
90%,
100% {
left: var(--var-anim-bounce-return-amount);
}
}
@keyframes bounce-inout-fact-alternate {
0% {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
}
50% {
--var-anim-bounce-inout-fact: var(--var-circle-size-space);
}
}
.circle:nth-child(1) {
--var-anim-delay-fact: -4;
}
.circle:nth-child(2) {
--var-anim-delay-fact: -3;
}
.circle:nth-child(3) {
--var-anim-delay-fact: -2;
}
.circle:nth-child(4) {
--var-anim-delay-fact: -1;
}
.circle:nth-child(5) {
--var-anim-delay-fact: -0;
}
.circle:last-child {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
--var-anim-bounce-return-amount: calc(-1 * var(--var-circle-count) * var(--var-circle-size-space));
position: relative;
animation: var(--var-color-cycle-anim), bounce-inout var(--var-anim-time-unit) var(--var-anim-bounce-func) infinite, bounce-return var(--var-anim-time-unit) ease-in-out infinite, bounce-inout-fact-alternate calc(2*var(--var-anim-time-unit)) steps(1) infinite;
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
可能的解决方法:添加只有 color-cycle
且没有移动动画的圆圈的复制版本,并让它们与原始圆圈重叠,并在动画迭代即将重新开始时将其可见性交换几毫秒.
我有一个理论,将所有内容组合到每个圆圈的一组关键帧中,而不是尝试将项目上的动画与圆圈动画同步,这可能有助于解决相位调整问题。
为了测试它,这里有一个片段可以粗略地重现给定代码的操作。其中的项目 div 没有动画。每个圆圈都保持其原始颜色,并向右、向上、向后、向右、向下、向后动画 - 两端几乎没有弹跳。
未观察到 'flashing/shadowing'。完成的处理量也更少。
关于问题中的代码,我的笔记本电脑显示平均 5% cpu 和 15% 的 gpu 使用率。在此片段中,它们分别约为 1.5% 和 11.5%。
.items {
--size: 16px;
--space: 8px;
--border: solid red;
--border-width: 0px;
--shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--count: 5;
--bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--time-unit: calc(1s);
--cycle-time: calc(2 * var(--count)*var(--time-unit));
--color-cycle-func: steps(1);
--color-1: #6db;
--color-2: #be4;
--color-3: #ec5;
--color-4: #d86;
--color-5: #f8c;
--slide-time: var(--time-unit);
--slide-func: linear;
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
width: calc((var(--size) + var(--space)) * var(--count));
height: var(--size);
position: relative;
}
.circle {
position: absolute;
width: var(--size);
height: var(--size);
border-radius: 100%;
box-shadow: var(--shadow);
display: inline-block;
margin: 0;
margin-right: var(--space);
box-sizing: border-box;
border: var(--border);
border-width: min(var(--border-width), calc(var(--size)/2));
animation: bounceround infinite var(--cycle-time) var(--slide-func);
animation-delay: var(--delay);
background-color: var(--color);
}
.circle:nth-child(1) {
--color: var(--color-1);
--delay: -8s;
}
.circle:nth-child(2) {
--color: var(--color-2);
--delay: -6s;
}
.circle:nth-child(3) {
--color: var(--color-3);
--delay: -4s;
}
.circle:nth-child(4) {
--color: var(--color-4);
--delay: -2s;
}
.circle:nth-child(5) {
--color: var(--color-5);
--delay: 0s;
}
@keyframes bounceround {
0% {
}
36.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
40% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(-100% - var(--size))); /* go up a bit */
}
41% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(-100% - (var(--size) / 2))); /* come down a fraction */
}
45% {
transform: translateX(0) translateY(-100%); /* move to the left */
}
49% {
transform: translateX(0) translateY(calc(var(--size) / 4)); /* jump down a bit*/
}
50% {
transform: translateX(0) translateY(0); /* go down to start position */
}
86.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
90% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(100% + var(--size))); /* go down a bit */
}
91% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(100% + calc(var(--size) / 2))); /* go up a fraction */
}
95% {
transform: translateX(0) translateY(100%); /* move to the left */
}
99% {
transform: translateX(0) translateY(calc(-1 * var(--size) / 4)); /* jump up a bit */
}
100% {
transform: translateX(0) translateY(0); /* back to where we started */
}
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>