【问题标题】:CSS animations out of sync by 1 frameCSS 动画不同步 1 帧
【发布时间】:2021-07-18 15:48:12
【问题描述】:

我有两个动画,分别称为 slidebounce-return,它们都应该以 0 延迟和 1 秒的持续时间播放。由于我怀疑动画处理逻辑中存在某种竞争条件,无论动画速度或刷新率如何,这些动画都会不同步 1 帧。我怎样才能阻止这些圈子跳进跳出?

(如果您没有立即发现任何问题,请在整页中查看 sn-p 或打开开发者工具。)

.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>

【问题讨论】:

  • 只是一个旁注,但为什么你的 CSS 变量有“var-”前缀?如果他们都明白了,那么在我的书中似乎是多余的。特别是当您使用它们时:var(--var-...).
  • 您是否尝试过只使用一组关键帧,这样事情就没有太多机会失步了。
  • @RickardElimää 我不知道为什么,但我从一开始就一直这样做。

标签: css css-animations chromium


【解决方案1】:

可能的解决方法:添加仅带有color-cycle 且没有移动动画的圆圈的重复版本,并让它们与原始圆圈重叠,并在动画迭代即将重新开始时将其可见性交换几毫秒。

【讨论】:

    【解决方案2】:

    我的理论是,将所有内容组合到每个圆圈的一组关键帧中,而不是尝试将项目上的动画与圆圈动画同步,这可能有助于消除定相问题。

    为了测试它,这里有一个 sn-p,它粗略地再现了给定代码的操作。其中项目 div 没有动画。每个圆圈都保持其原始颜色,并在右侧、向上、向后、向右、向下、向后移动 - 每一端几乎没有反弹。

    没有观察到“闪烁/阴影”。完成的处理量也更少。

    在问题中的代码中,我的笔记本电脑显示平均大约 5% 的 cpu 和 15% 的 gpu 使用率。在这个 sn-p 上,它们分别约为 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>

    【讨论】:

    • 我怎么没想到!非常感谢。
    • 很高兴它很有用 - 这是一个有趣的动画。
    • 看到这一点,我不必要地划分任务,使它变得更加复杂。希望你有一个好的。
    猜你喜欢
    • 2017-08-14
    • 2013-07-17
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多