使音频进度条过渡更平滑

3

我在我的网站上为音频对象制作了一个简单的自定义进度条。 进度条工作正常,但是我注意到它非常卡顿。 我还注意到在Facebook和YouTube等网站上,他们的进度条过渡似乎异常流畅(观看任何视频,您都会知道我的意思)。

我认为解决这个问题的方法可能是使用一些巧妙的JavaScript和CSS,但最终它似乎很俗气,没有原因地消耗CPU,并且看起来实际上与以前完全相同。(这是我想出的解决方案):

setInterval(function(){
    var rect = elapsedContainer.getBoundingClientRect();
    var percentage = audio.currentTime / audio.duration;
    elapsed.style.width = (percentage * rect.width) + "px";
}, 33); // 30fps

.elapsed-container{
    width: 100%;
    height: 10px;
    background: grey;
}
.elapsed{
    left: 0;
    height: 100%;
    background: red;
    transition: width 33ms linear;
}

JsFiddle

欢迎所有帮助,感谢。


3
请将代码转换为代码片段,以便我们可以运行它。或者将链接粘贴到CodePen或Sandbox中。谢谢。 - Artem Arkhipov
当然,但我的意思是它相当容易理解。 - GROVER.
4个回答

3
您可以尝试使用 window.requestAnimationFrame() 代替 setTimeout()。requestAnimationFrame 回调允许计算机尽可能接近60帧每秒,但可以根据负载修改帧率,使其比 setTimeout() 更高效率,后者必须始终匹配指定的帧率,否则可能会跳过帧并出现 "闪烁" (有关详细信息请参见这里)。
我还删除了CSS过渡,以避免混合动画(因为requestAnimationFrame已经以60fps进行动画处理,所以CSS过渡有点不相关)。
// Change setTimeout to requestFrameAnimation
function progress_animation() {
  var rect = container.getBoundingClientRect();
  var percentage = audio.currentTime / audio.duration;
  elapsed.style.width = (percentage * rect.width) + "px";

  window.requestAnimationFrame(progress_animation);
};

// Only run animation when relevant
document.getElementById("play").onclick = function(){
  window.requestAnimationFrame(progress_animation);

  audio.play();
}

https://jsfiddle.net/4ymch2jg/

对我来说,这个看起来更流畅。


聪明的想法。但在我看来仍然有些生硬。对于使用rAF的想法给予+1的支持。 - GROVER.

2
想法是使用requestAnimationFrame而不是简单的setInterval。您可以在这里阅读更多相关信息。 主要的问题是,这种方法优化了浏览器的渲染能力和资源使用情况。这就是为什么它看起来更加流畅的原因。
以下是代码示例,您可以根据需要进行扩展。
let duration = 20000;
let startTime = Date.now();
const elapsedContainer2 = document.querySelector('#con-2');
const elapsed2 = document.querySelector('#el-2');

function animate() {
  var rect = elapsedContainer2.getBoundingClientRect();
    let percentage = (Date.now()-startTime) / duration;
    elapsed2.style.width = (percentage * rect.width) + "px";
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

这里是codepen链接,您可以看到两种方法之间的差异。顺便提一下,您的机器负载越高,差异就越明显。
请注意,我还将CSS动画从您的情况下的left 33ms更改为width 0.16s。在代码中,我们更改
width而不是left属性,因此应在CSS动画中提到它。0.16s接近60fps,这正是requestAnimationFrame试图实现的。
.elapsed{
    left: 0;
    height: 100%;
    background: red;
    transition: width 0.16s linear;
}

0

1
如果您能创建一个可工作的示例并附加它,那就太好了。 - Artem Arkhipov
一个工作示例会很好。唯一的问题是我不知道距离,因为用户随时可以选择暂停歌曲。 - GROVER.

0

在设置新的宽度之前,你必须等待 CSS 过渡结束。因此,你需要使用 transitionend 事件。这里有一个例子:

var percentDone = 0;
var elapsed =  document.getElementsByClassName("elapsed")[0];
setProgress = function() {
    percentDone++;
    if( percentDone<=100 ) {
        elapsed.style.width = percentDone + "%";
    };
};
elapsed.addEventListener('transitionend', setProgress);
setProgress();
.elapsed-container{
    width: 100%;
    height: 10px;
    background: grey;
}
.elapsed{
    left: 0;
    width: 0;
    height: 100%;
    background: red;
    transition: width 33ms linear;
}
<div class="elapsed-container">
    <div class="elapsed"></div>
</div>

你可以在JSFiddle上看到它的运行效果。


这非常顺畅!你知道转换事件的支持是什么样的吗? - GROVER.
它得到了很好的支持,也在caniuse上有记录,如下所示:列出的支持是针对过渡属性以及过渡结束事件的。 - skobaljic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接