使用while(Date.now()< interval){}和requestAnimationFrame()

时间:2016-01-16 14:47:06

标签: javascript frame-rate

我们都知道如果某些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();

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

3 个答案:

答案 0 :(得分:0)

在您的特定场景中,使用setTimeout延迟mainloop执行可能会更好,如下所示:

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

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

答案 1 :(得分:0)

使用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();

答案 2 :(得分:0)

您可以使用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。