为什么毫秒计数器在requestAnimationFrame()中持续运行?

时间:2019-11-21 21:53:24

标签: javascript requestanimationframe

我正在使用强大的requestAnimationFrame(callback)函数使用JavaScript进行游戏。

今天,我了解到自打开该页面以来,传递给requestAnimationFrame()的回调获得了高分辨率的时间。我们称之为ms

function paint(ms) {
    // draw my game
    requestAnimationFrame(paint);
}

requestAnimationFrame(paint);

这太好了,但是有一件事我不太明白。

当我们转到另一个选项卡时,功能requestAnimationFrame()不执行任何操作,因此渲染已暂停。另一方面,离开时传递给回调的时间仍然继续。因此,我不确定如何使用该值。如果它与渲染引擎成比例地工作,我可以使用ms来计算游戏的逻辑时间,因为依靠requestAnimationFrame()作为稳定的60 FPS听起来并不是最好的主意。

我想念什么吗? ms参数的用途是什么,如果我们离开标签页时它继续计数?

2 个答案:

答案 0 :(得分:1)

那只是一个时间戳,它实际上并没有算什么。实际上,它实际上与performance.now()相同,后者给出了自页面处于活动状态以来的时间。至于原因,这就是DOMHighResTimeStamp's origin的定义方式。

我们通常用它来知道自从发生某个先前事件以来已经过了多长时间,也就是说,我们存储一个start_time,然后检查当前时间戳,无论实际情况如何,我们都可以获取动画的增量时间帧速率。

顺便说一句,不,依靠requestAnimationFrame处于任何固定帧速率确实不是一个好主意,requestAnimationFrame不受规范的任何帧速率限制,实际上建议将其与屏幕刷新率对齐(尽管只有“闪烁”才可以)。

因此,实际上您需要依靠这样的时间戳才能在不同设置之间保持一致的速度。在每帧上进行简单的pos++处理,可使动画在120Hz显示器上的运行速度比60Hz显示器(至少在Chrome上)快两倍。

这只会使您的想法更难以理解如何实现:当窗口移至120Hz监视器时,您的绘画计时器会快两倍吗?

此时间戳也可能有助于赶上长帧,这不是因为系统出现打ic,您不一定希望动画持续更长的时间。
同样,并非所有情况都希望窗口模糊时动画逻辑停止。假设我有一个带有由rAF驱动的背景动画的标题,我真的不希望它离开屏幕时暂停,即使它没有被绘制。

如果您希望游戏逻辑在窗口模糊时暂停,请监听onvisibilitychange,并保存当前时间戳(使用performance.now(),因为我们不在rAF范围内。)

答案 1 :(得分:1)

添加到Kaiido的答案中。在应用程序中限制时间增量也很常见。例如,应用中帧速率独立计算的一种常见方式是计算帧之间的时间

let previousTime = 0;

function loop(currentTime) {
  const deltaTime = currenTime - previousTime;
  previousTime = currentTime;

  // use deltaTime in various calculations
  posX = posX + velX * deltaTime;

  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

但是,如果deltaTime太大,有时碰撞和/或其他数学运算会中断,因此游戏通常会添加限制器

  // don't let deltaTime be more than a 1/10 of a second
  const deltaTime = Math.min(currenTime - previousTime, 1000 / 10);

这基本上消除了检查onvisiblitiychange的需要。尤其是如果您保留自己的动画/游戏时钟。

let previousTime = 0;
let clock = 0;
let clockRate = 1;

function loop(currentTime) {
  const deltaTime = Math.min(currenTime - previousTime, 1000 / 10) * clockRate;
  previousTime = currentTime;

  clock += deltaTime;  // update our own clock

  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

现在,如果玩家隐藏标签,不仅时钟不会跳动,或者如果时钟暂停,我们也可以将clockRate设置为0。

为什么要使用自己的时钟?它使您可以轻松降低或加快所有依赖该时钟的计算的时间(请参见上面的clockRate)。同样,许多应用(游戏)具有多个时钟。他们将为每个时钟计算不同的deltaTime。例如,即使应用程序/游戏已暂停,所有需要暂停的对象的时钟还是时钟都需要继续前进。另一个可能是玩家的时钟与敌人的时钟,因此当玩家使用其“慢时间超级力量”时,敌人的动作变慢,但玩家的动作保持相同的速度。