首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >setTimeout对requestAnimationFrame

setTimeout对requestAnimationFrame
EN

Stack Overflow用户
提问于 2022-03-18 06:02:46
回答 1查看 369关注 0票数 0

我做了一个示例的'setTimeout vs requestAnimationFrame‘,以了解他们有多不同。

如你所见,橙色盒子首先到达目的地。绿色盒子跳了几次,跳得更慢。

我明白为什么绿盒子会跳几次。因为任务(调用move函数)在重新绘制之前不会插入到macroTaskQueue中(这称为jank或frame跳过)。

这就是为什么我更喜欢requestAnimationFrame而不是setTimeout时动画元素。因为保证在重新绘制之前调用move() of requestAnimationFrame(move)

现在,我想知道的是,为什么绿色盒子比橙色盒子

这是否意味着move()不是在每1000/60毫秒时被调用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-18 07:26:31

setTimeout总是迟到。

它的工作方式是

  • 注册一个时间戳何时执行我们的任务。
  • 在每个事件循环的迭代中,检查现在是否在时间戳之后。
  • 执行任务。

根据这个设计,setTimeout()必须至少占用延迟所定义的时间。它可以(而且经常)更多,例如,如果事件循环忙着做其他事情(比如处理用户手势、调用垃圾收集器等等)。

现在,由于您只在调用以前的回调时才请求新的超时,所以您的setTimeout()循环会受到时间漂移的影响。每一次迭代,它都会累积这个漂移,并且永远无法从它中恢复过来,远离挂钟的时间。

另一方面,requestAnimationFrame (rAF)并没有受到这样的影响。事实上,监视器的V信号就是告诉事件循环何时必须进入"更新呈现“步骤的信号。此信号不绑定到CPU活动,并将作为一个稳定的时钟工作。如果在一个帧中,rAF回调延迟了几毫秒,那么下一个帧在中间的时间就会更少,但是标志将在没有漂移的情况下定期设置。

您可以通过提前安排所有计时器来验证这一点,您的setTimeout盒将不再受此影响:

代码语言:javascript
复制
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();
}
代码语言:javascript
复制
.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;
}
代码语言:javascript
复制
<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监视器上运行它。具有较高或较低刷新率的监视器将在不同频率进入此“更新呈现”步骤。

还要注意,delaysetTimeout(fn, delay)中是一个long,这意味着您传递的值将被泛泛为整数。

最后一点,Chrome在它的setInteval()实现中做了自我修正,火狐和规范仍然没有,但它是在(不那么积极的)讨论中

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71523029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档