请参见以下代码段。
单击按钮即可创建一个加载器。但是动画不流畅。
我最近阅读了有关requestAnimationFrame
函数的信息,该函数可以完成此工作。但是,由于无法在setInterval
函数中指定时间,因此如何使用它完全替换requestAnimationFrame
。
它可以与setInterval
结合使用吗?
let idx = 1;
const timetoEnd = 5000;
function ProgressBar(width){
this.width = width || 0;
this.id = `pBar-${idx++}`;
this.create = () => {
let pBar = document.createElement('div');
pBar.id = this.id;
pBar.className = `p-bar`;
pBar.innerHTML = `<div class="loader"></div>`;
return pBar;
};
this.animator = () => {
let element = document.querySelector(`#${(this.id)} div`);
if(this.width < 100){
this.width++;
element.style.width = `${this.width}%`;
} else {
clearInterval(this.interval);
}
};
this.animate = () => {
this.interval = setInterval(this.animator, timetoEnd/100);
}
}
function addLoader (){
let bar1 = new ProgressBar(40);
let container = document.querySelector("#container");
container.appendChild(bar1.create());
bar1.animate();
}
.p-bar{
width: 400px;
height: 20px;
background: 1px solid #ccc;
margin: 10px;
overflow:hidden;
border-radius: 4px;
}
.p-bar .loader{
width: 0;
background: #1565C0;
height: 100%;
}
<input type="button" value="Add loader" onclick="addLoader()" />
<div id="container"></div>
答案 0 :(得分:1)
您是对的,requestAnimationFrame
是在制作动画时避免UI阻塞的推荐方法。
您可以记住开始时的绝对开始时间,而不是尝试在每个帧中都这样做。然后,只需根据开始时间和当前时间之间的增量时间计算宽度即可。
此外,document.querySelector
被认为是相对“繁重”的操作,因此我添加了this.element
以避免在每一帧都这样做。
以下是计算新宽度的方法:((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth
100 - this.startWidth
是我们必须设置动画的总宽度(100 - this.startWidth) / timetoEnd
是每秒必须增加多少宽度(1)((100 - this.startWidth) / timetoEnd) * deltaT
是我们必须增加(1)的宽度this.startWidth
px移至具有框架的宽度还请注意,其中一些计算是恒定的,不必在每个帧上都进行计算,这是我在练习中留下的:)
这是您稍作修改的代码:
let idx = 1;
const timetoEnd = 5000;
function ProgressBar(startWidth){
this.startWidth = startWidth || 0;
this.id = `pBar-${idx++}`;
this.create = () => {
let pBar = document.createElement('div');
pBar.id = this.id;
pBar.className = `p-bar`;
pBar.innerHTML = `<div class="loader"></div>`;
return pBar;
};
this.animator = () => {
const deltaT = Math.min(new Date().getTime() - this.start, timetoEnd);
if(deltaT < timetoEnd){
const width = ((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth;
this.element.style.width = `${width}%`;
requestAnimationFrame(this.animator.bind(this))
}
};
this.animate = () => {
this.element = document.querySelector(`#${(this.id)} div`);
this.start = new Date().getTime();
this.animator();
}
}
function addLoader (){
let bar1 = new ProgressBar(40);
let container = document.querySelector("#container");
container.appendChild(bar1.create());
bar1.animate();
}
.p-bar{
width: 400px;
height: 20px;
background: 1px solid #ccc;
margin: 10px;
overflow:hidden;
border-radius: 4px;
}
.p-bar .loader{
width: 0;
background: #1565C0;
height: 100%;
}
<input type="button" value="Add loader" onclick="addLoader()" />
<div id="container"></div>