如何使用 requestAnimationFrame 锁定 FPS?

How to lock FPS with requestAnimationFrame?

我使用了 Paul Irish 的剧本 https://gist.github.com/paulirish/1579671 在 html 站点内创建动画循环。

尽管它在全屏模式下比在浏览器中更快 window,但它仍然有效。 此外,我观察到不同的速度取决于 canvas 大小和我使用的浏览器。

问:如何使用脚本保证稳定的帧率?

此处提供代码(WebGL 入门,第 1 章,作者 Brian Danchilla): https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html

您无法直接强制执行稳定的帧速率。您的页面并不是用户平台上唯一 运行ning 的应用程序,而且平台功能差异很大。 requestAnimationFrame 运行 尽可能快,不超过目标设备上的显示更新间隔,但可能会慢得多,具体取决于可用 CPU、GPU、内存和其他限制。

这里的标准做法是测量自上一个动画帧以来经过的时间量,通常使用 Date.now(),并且每个帧将动画推进该时间量。对于人眼来说,这使得生成的动画 运行 具有一致的速度,即使帧速率变化很大。

例如,Shadertoy and GLSL Sandbox 运行 full-screen GLSL着色器等站点并传入一个名为time(或iGlobalTime)的制服,这是float 表示自着色器启动以来经过的秒数。此时间值会以不规则的间隔增加,具体取决于每个动画帧渲染所用的时间,但结果是浮点数似乎以每秒 1.0 的稳定数向上计数。这样,根据这个时间值的shader播放就可以显得一致了。

像这样的东西应该有用。如果两帧之间的时间差小于您的 FPS 限制,更新函数 returns 并等待下一帧。但这只会限制更新发生得太快;正如 emackey 所说,更新循环总是有可能 运行 更慢。

var updateId,
    previousDelta = 0,
    fpsLimit = 30;

function update(currentDelta) {
    updateId = requestAnimationFrame(update);

    var delta = currentDelta - previousDelta;

    if (fpsLimit && delta < 1000 / fpsLimit) {
        return;
    }

    /* your code here */

    previousDelta = currentDelta;
}

为了美化@emackey 所说的话,

简短的回答是你不能。您可以要求计算机每一帧做无限量的工作。我不能保证在有限的时间内完成这项工作。

最重要的是,每台计算机的功率不同。便宜的集成 GPU 的功率比高端显卡低得多。 intel i3 比 i7 慢得多。

您还提到更改 canvas 大小。绘制 300x150 canvas 仅需 45000 像素的工作量。绘制 1920x1080 canvas 将需要 2,073,600 像素的工作量或 46 倍以上的工作量

您能做的最好的事情就是做尽可能少的工作,或者自动或根据用户选择删除慢速硬件上的功能。大多数游戏都是这样做的。它们是图形设置选项,用户可以在其中选择分辨率、纹理分辨率、anti-alising 级别和各种其他内容。

也就是说,您可以尝试进行计算,使应用中的事物相对于时间以一致的速度移动。在慢速机器或更大的机器上帧率可能会变慢 canvas 但每秒移动的距离将保持不变。

您可以使用传递给 requestAnimationFrame

的时间值来执行此操作
function render(time) {
   // time is time in milliseconds since the page was loaded

   ...do work...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

比如这里是帧率独立动画

function render(time) {

   xPosition = xPosition + velocity;

   ...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

这里是帧率独立动画

var then = 0;
function render(time) {
   var timeInSeconds = time * 0.001;
   var deltaTimeInSeconds = timeInSeconds - then;
   then = timeInSeconds;

   xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;

   ...

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

注意:传入requestAnimationFrame的时间分辨率比Date.now()

Here's an article on it with animations