Using while(Date.now() < interval) {} with requestAnimati

Using while(Date.now() < interval) {} with requestAnimationFrame()

本文关键字:interval with requestAnimati while Date now Using lt      更新时间:2023-09-26

我们都知道,如果某些 fps 很重要或类似的东西,制作正确的更新算法是多么困难。

无论如何,我只是想出了这个无限的循环黑客,它只是将程序冻结到下一帧,它似乎完美无缺。

var then = Date.now()
var fps = 40;
var interval = 1000 / fps;
function mainloop() {
     while (Date.now() - then < interval) {}  // freezes program until next frame
     requestAnimationFrame(mainloop);
     then = Date.now();
     // update logic goes here
}
mainloop();

我在任何地方都没有看到这个解决方案,所以我想问它是否干净和正确。我知道冻结程序只是为了等待某些东西是不好的,那段代码看起来很糟糕,但它似乎有效。有没有一个更干净的解决方案可以与我的代码类似?

您可以使用 setTimeout 等待一定时间,但这不会非常精确。但是,通过一直更改interval,您可以获得足够精确的平均延迟。

var startTime = Date.now();
var fps = 40;
var frames = 0;
var interval = 1000 / fps;
function mainloop() {
    frames++;
    var timeElapsed = Date.now() - startTime,
        averageFps = 1000 * frames / timeElapsed;
    if(averageFps < fps && interval > 0) interval -= 0.1;
    if(averageFps > fps) interval += 0.1;
    setTimeout(mainloop, interval);
    // update logic goes here
}
setTimeout(mainloop, interval);

但是,如果速度太慢,计算机仍然无法满足请求的 fps 的风险。

使用 while 循环来浪费时间是一个坏主意。它只是浪费了处理器时间,而这些时间本可以做其他事情。

按照 jishi 的建议使用 SetTimeout 是一种可能的解决方案。但是,这仅控制代码的运行时间。您无法真正控制浏览器实际绘制的时间。基本上,浏览器将绘制代码更新的最后一帧。

因此,另一种可能的解决方案是使用 requestAnimation。在绘图代码中,确定以首选速率出现的最后一帧。画那个框架。例如。。。

var start = null;
var fps = 40;
var interval = 1000 / fps;
function mainloop(timeStamp) {
    if (!start) {
        start = timeStamp;
    }
    var n = (timeStamp - start) / interval;
    // update logic goes here to paint nth frame
    requestAnimationFrame(mainloop);
}
mainloop();

在您的特定场景中,最好使用 setTimeout 延迟主循环执行,如下所示:

var nextExecution = Date.now() - then + interval;
if (nextExecution < 0) nextExecution = 0;
setTimeout(mainloop, nextExecution);

这将允许它在等待下一帧渲染时执行其他操作。