【问题标题】:Change width of element without reflow在不回流的情况下更改元素的宽度
【发布时间】:2016-05-20 07:01:56
【问题描述】:

我有一个带有动画条的音乐播放器,它显示歌曲中的当前位置。它使用requestAnimationFrame 渲染,并将div 的width 样式更改为X%,其中X 是当前歌曲播放时间的百分比。

我相信这会在 Chrome 中导致大量 CPU 使用,因为每帧都会进行持续的回流工作。我可以使用哪些其他选项来消除回流和减少 CPU?

另外两个要求:此网页是后端音乐服务器上的 Web UI。它没有使用任何 HTML5 媒体元素。因此,页面可能会在歌曲部分结束时加载,因此位置不会始终在 0 到 100 之间进行动画处理。

下面的小提琴在我的机器上显示大约 30% 的 CPU,这对于动画矩形来说似乎有点高。

var pos = 0;
var s = document.getElementById('i');
f = function() {
  window.requestAnimationFrame(f);
  pos += .03;
  s.style.width = pos + '%';
}
f();
#i {
  background-color: red;
  position: absolute;
}
<div id="i">&nbsp;
</div>

【问题讨论】:

  • 为什么是setAttribute?只需使用s.style.width = pos + '%';
  • @Oriol 改为那个;没有任何帮助
  • @RayonDabre 因为我不知道其他方式。

标签: javascript css reflow


【解决方案1】:

CSS:

#progress-bar {
  background-color: red;
  height: 10px;
  width: 100%;
  transform-origin: 0 0;
}

JS:

'use strict'

var progressBar = document.getElementById('progress-bar')

function setProgress(percentage) {
  requestAnimationFrame(function () {
    progressBar.style.transform = 'scaleX(' + percentage + '%)'
  })
}

setProgress(10)

将宽度设置为 100% 时,您会得到一个全宽的彩色条。

然后我们可以应用缩放变换来设置条的宽度而不进行回流。

但是哦,它会扩展到中间。我们可以通过使用transform-origin: x y 将变换的原点设置为左上角来解决这个问题,其中 x 和 y 为 0。

然后我们将样式更改包装在 requestAnimationFrame 中,让浏览器优化何时应用更改。

砰!你有一个高性能的零回流进度条。

【讨论】:

  • 您应该在回答中提供更多信息。也许是对你在做什么的解释。简单地发布答案将导致您的帖子被否决或删除。
  • @Chris 更好? :)
【解决方案2】:

您呈现的脚本与您想要的脚本不是很相关,您在requestAnimationFrame 上制作动画,但实际上每次“歌曲百分比”变化时您都会制作动画。

假设您有一个返回当前播放歌曲百分比的函数(例如getSongPer()):

var oldPos, pos = 0;
var s = document.getElementById('i');
f = function() {
  oldPos = pos;
  pos = getSongPer();
  if(oldPos !== pos){
    s.style.width = pos + '%';
  }
  if(pos<100){
    window.requestAnimationFrame(f);
  }
}
f();

我没有测试它,但我希望它更轻,而且性能会受到百分比精度的影响,例如如果您的数字精度为零,则将有大约 100 次动画更改,之后的每个数字大约会增加十倍。

【讨论】:

    【解决方案3】:

    您可以通过多种方式制作不会导致重新布局的纯 CSS 进度条,以下是一些示例:

    1. animation - http://jsbin.com/yoqabe/edit?html,css,js,output

      我认为最有效的方法之一是使用动画来控制线性渐变的背景位置。唯一的缺点是您只能播放/暂停动画。

    2. background-position - http://jsbin.com/veyibe/edit?html,css,js,output

      如果您需要使用 JS 更新位置的能力,那么我建议更新渐变的 background-position 并应用 CSS 过渡,去抖动以避免更新过快。

    3. translateX: http://jsbin.com/zolurun/edit?html,js,output

      您还可以使用 CSS 转换在容器内滑动进度条,这也应该避免重绘。

    这些链接也可能有用:

    【讨论】:

      【解决方案4】:

      您可以考虑使用其他不需要布局操作的 CSS 属性,例如 background-size

      并使用 CSS 动画而不是 requestAnimationFrame

      var bar = document.getElementById('i');
      function playSong(currentTime, duration) {
        bar.style.animationDuration = duration + 's';
        bar.style.animationDelay = - currentTime + 's';
      }
      playSong(3, 10);
      #i {
        height: 1em;
        background-image: linear-gradient(red, red);
        background-repeat: no-repeat;
        animation: bar linear;
      }
      @keyframes bar {
        from { background-size: 0% 100%; }
        to { background-size: 100% 100%; }
      }
      &lt;div id="i"&gt;&lt;/div&gt;

      【讨论】:

      • 如果一首歌已经完成了一半怎么办?我需要酒吧从 50% 开始,而不是在一半的时间内从 0 到 100。 (这首歌不是从标签播放的,标签只是音乐服务器上的一个 UI。)
      • @mjibson 你可以使用动画属性来实现。
      【解决方案5】:

      使用timeupdate,元素的currentTime属性指示的时间发生了变化。

      试试这个:

      var audio = document.getElementById("audio");
      
      function updateProgress() {
        var progress = document.getElementById("progress");
        var value = 0;
        if (audio.currentTime > 0) {
          value = Math.ceil((100 / audio.duration) * audio.currentTime);
        }
        progress.style.width = value + "%";
      }
      
      
      audio.addEventListener("timeupdate", updateProgress, false);
      #progressBar {
        border: 1px solid #aaa;
        color: #fff;
        width: 295px;
        height: 20px;
      }
      #progress {
        background-color: #ff0000; // red
        height: 20px;
        display: block;
        height: 100%;
        width: 0;
      }
      <div id="progressBar"><span id="progress"></span>
      </div>
      <audio id="audio" controls>
        <source src="http://www.w3schools.com/tags/horse.ogg" type="audio/ogg" />
        <source src="http://www.w3schools.com/tags/horse.mp3" type="audio/mpeg" />Your browser does not support the audio element.
      </audio>

      【讨论】:

      • timeupdate 仅适用于 HTML5 音频元素,而我的事情不是。网页是后端音乐服务器上的 UI。
      【解决方案6】:

      如果您在进度条本身上使用position: absoluteposition: fixed,它应该可以防止页面上出现大量回流。

      【讨论】:

      • 那么你的页面上肯定有其他东西正在运行导致它。你在运行什么JS?我有一种感觉,你的 JS 中有一些密集的东西会导致你遇到的延迟,而不仅仅是元素调整大小本身。
      猜你喜欢
      • 2017-02-22
      • 1970-01-01
      • 2017-11-14
      • 1970-01-01
      • 2020-09-13
      • 2012-04-17
      • 1970-01-01
      • 2011-05-05
      相关资源
      最近更新 更多