如何在 CSS 宽度变化时实现流畅的 CSS 动画?

How to achieve smooth CSS animation on CSS width change?

我正在制作自己的进度条,它将在 5 分钟到 1 秒的不同时间段内填满。我的想法是:

  1. 我有一个栏,其 CSS 宽度设置为 0%,每隔 x 秒
  2. 然后我使用 JS 将条形宽度增加 1%。

它是这样工作的:https://jsfiddle.net/dnh4y39c/

这种方法存在一些问题:

  1. 动画仅在 <2 秒内流畅(100 次间隔 20 毫秒), 除此之外,它显然不够平滑,请参阅: https://jsfiddle.net/dnh4y39c/1/ ,

  2. 我可以用 CSS 转换来解决这个问题,但它完全崩溃了 计时,如果我在栏上设置 transition: all 0.5s; 那么它就可以工作 在更长的时间内非常好,但它完全坏了更快 负载,例如。进度条应该在 1 秒内完成,但它会停止 持续 0.5 秒,然后一次全部设置 100% 动画,请参阅: https://jsfiddle.net/dnh4y39c/1/

我想知道我是否应该 fiddle 宽度或者只是移动到 canvas?我不知道 canvas 有那么好,但我想它可能性能更高并且占用更少的资源?我的应用程序中可能有多个进度条,因为我需要单独加载所有模块(因此没有一个简单的加载器,但在某些地方甚至可能超过 50 个 - 我知道,这很疯狂,但我只是在关注文档)。

感谢任何意见:)

需要考虑的几件事是:

  • 避免使用 setInterval(),如果间隔回调的执行时间长于间隔本身的持续时间,它会产生 "jumpy" 动画行为。如果需要帧间延迟,请改为使用 setTimeout()
  • 考虑使用 window.requestAnimationFrame() 更新进度条的动画。使用 window.requestAnimationFrame() 可确保您的进度条的更新与浏览器 repaint/redraw 周期
  • 同步

您的代码的更新版本,将这两个想法考虑在内,可能如下所示:

function interation(element, iterationDuration, i) {

  window.requestAnimationFrame(function() {

    /*
    Update the width and transition duration of the element for this iteration
    */
    element.style.width = `${i}%`;
    element.style.transitionDuration = `${iterationDuration}ms`;

    /*
    Increment/calculate next percentage value for progress bar
    */
    const next = (i === undefined ? 0 : i) + 1;
    if (next <= 100) {

      /* 
      Pass element, timeout, and next value through as arguments
      to next interation() call
      */
      setTimeout(interation, iterationDuration, element, iterationDuration, next);
    }
  })
}

interation(document.querySelector(".progress__bar"), 20);
// interation(document.querySelector(".progress__bar"), 2000); Long interval
// interation(document.querySelector(".progress__bar"), 20); Short interval
* {
  box-sizing: border-box;
}

.progress__bar {
  transition: width linear;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  border-radius: 4px;
  background: #f00;
}
<div class="item">
  <div class="item__progress">
    <div class="progress__bar" style="width: 0%;"></div>
  </div>
</div>