无需回流即可更改元素的宽度

时间:2016-02-10 03:54:58

标签: javascript css reflow

我有一个带有动画条的音乐播放器,可以显示歌曲中的当前位置。它使用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>

6 个答案:

答案 0 :(得分: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. 这些链接也可能有用:

答案 1 :(得分:1)

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

使用CSS动画代替requestAnimationFrame

&#13;
&#13;
var bar = document.getElementById('i');
function playSong(currentTime, duration) {
  bar.style.animationDuration = duration + 's';
  bar.style.animationDelay = - currentTime + 's';
}
playSong(3, 10);
&#13;
#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%; }
}
&#13;
<div id="i"></div>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

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

答案 3 :(得分:0)

  

使用timeupdate,元素的currentTime属性指示的时间已更改。

试试这个:

&#13;
&#13;
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);
&#13;
#progressBar {
  border: 1px solid #aaa;
  color: #fff;
  width: 295px;
  height: 20px;
}
#progress {
  background-color: #ff0000; // red
  height: 20px;
  display: block;
  height: 100%;
  width: 0;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 4 :(得分:0)

您呈现的剧本与您想要的剧本并不相关,您在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个动画更改,对于之后的每个数字,将会有大约十倍的动画更改。

答案 5 :(得分:0)

CSS:

function getToken(params) {
 return new Promise((resolve, reject) => {
  request.post(params, function(err, res, body) {
   resolve(JSON.parse(body).token);
  })
 })
}


 function getDevice(token, id, callback) {
  request.get(..., function(err, res, body){
   callback(err, body)
  })
 }

function async myExecutionMethod() {
 let token = await getToken(myParams) // Should be a String of my token
 let device = getDevice(token, 1234, cb)
}

myExecutionMethod();

JS:

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

将宽度设置为100%时,您将获得全宽彩色条。

然后我们可以应用比例变换来设置条形的宽度而不需要回流。

但是哦,它可以扩展到中间。我们可以通过使用'use strict' var progressBar = document.getElementById('progress-bar') function setProgress(percentage) { requestAnimationFrame(function () { progressBar.style.transform = 'scaleX(' + percentage + '%)' }) } setProgress(10) 将变换的原点设置到左上角来解决这个问题,x和y为0。

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

的Bam!您有一个高性能的零回流进度条。