我做了一个示例的'setTimeout vs requestAnimationFrame‘,以了解他们有多不同。
如你所见,橙色盒子首先到达目的地。绿色盒子跳了几次,跳得更慢。
我明白为什么绿盒子会跳几次。因为任务(调用move函数)在重新绘制之前不会插入到macroTaskQueue中(这称为jank或frame跳过)。
这就是为什么我更喜欢requestAnimationFrame而不是setTimeout时动画元素。因为保证在重新绘制之前调用move() of requestAnimationFrame(move)。
现在,我想知道的是,为什么绿色盒子比橙色盒子慢
这是否意味着move()不是在每1000/60毫秒时被调用?
发布于 2022-03-18 07:26:31
setTimeout总是迟到。
它的工作方式是
根据这个设计,setTimeout()必须至少占用延迟所定义的时间。它可以(而且经常)更多,例如,如果事件循环忙着做其他事情(比如处理用户手势、调用垃圾收集器等等)。
现在,由于您只在调用以前的回调时才请求新的超时,所以您的setTimeout()循环会受到时间漂移的影响。每一次迭代,它都会累积这个漂移,并且永远无法从它中恢复过来,远离挂钟的时间。
另一方面,requestAnimationFrame (rAF)并没有受到这样的影响。事实上,监视器的V信号就是告诉事件循环何时必须进入"更新呈现“步骤的信号。此信号不绑定到CPU活动,并将作为一个稳定的时钟工作。如果在一个帧中,rAF回调延迟了几毫秒,那么下一个帧在中间的时间就会更少,但是标志将在没有漂移的情况下定期设置。
您可以通过提前安排所有计时器来验证这一点,您的setTimeout盒将不再受此影响:
const startBtn = document.querySelector('#a');
const jankBtn = document.querySelector('#b');
const settimeoutBox = document.querySelector('.settimeout-box');
const requestAnimationFrameBox = document.querySelector('.request-animation-frame-box');
settimeoutBox._left = requestAnimationFrameBox._left = 0;
let i = 0;
startBtn.addEventListener('click', () => {
startBtn.classList.add('loading');
startBtn.classList.add('disabled');
scheduleAllTimeouts(settimeoutBox);
moveWithRequestAnimationFrame(requestAnimationFrameBox);
});
function reset() {
setTimeout(() => {
startBtn.classList.remove('loading');
startBtn.classList.remove('disabled');
i = 0;
settimeoutBox.style.left = '0px';
requestAnimationFrameBox.style.left = '0px';
settimeoutBox._left = requestAnimationFrameBox._left = 0;
}, 300);
}
function move(el) {
el._left += 2;
el.style.left = el._left + 'px';
if (el._left > 1000) {
return false;
}
return true;
}
function scheduleAllTimeouts(el) {
for (let i = 0; i < 500; i++) {
setTimeout(() => move(el), i * 1000 / 60);
}
}
function moveWithRequestAnimationFrame(el) {
if (move(el)) {
requestAnimationFrame(() => {
moveWithRequestAnimationFrame(el);
});
} else reset();
}.grid {
margin: 30px !important;
padding: 30px;
}
.box {
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
color: white;
font-size: 18px;
}
.settimeout-box {
background-color: green;
}
.request-animation-frame-box {
background-color: orange;
}<div class="ui grid container">
<div class="row">
<button class="ui button huge blue" id="a">Start!</button>
</div>
<div class="row">
<div class="box settimeout-box">
<span>setTimeout</span>
</div>
</div>
<div class="row">
<div class="box request-animation-frame-box">
<span>requestAnimationFrame</span>
</div>
</div>
</div>
请注意,Firefox和Chrome实际上在非动画文档中第一次调用rAF之后就触发了画框,因此rAF可能比本演示中的setTimeout早一帧。
requestAnimationFrame的频率与监视器的刷新率有关。
上面的示例假设您在60 on监视器上运行它。具有较高或较低刷新率的监视器将在不同频率进入此“更新呈现”步骤。
还要注意,delay在setTimeout(fn, delay)中是一个long,这意味着您传递的值将被泛泛为整数。
最后一点,Chrome在它的setInteval()实现中做了自我修正,火狐和规范仍然没有,但它是在(不那么积极的)讨论中。
https://stackoverflow.com/questions/71523029
复制相似问题