如何在不同浏览器上解决requestAnimationFrame中不同的FPS?

Posted

技术标签:

【中文标题】如何在不同浏览器上解决requestAnimationFrame中不同的FPS?【英文标题】:How to solve different FPS in requestAnimationFrame on different browsers? 【发布时间】:2011-12-02 15:54:32 【问题描述】:

如何解决requestAnimationFrame在不同浏览器上的不同FPS? 我正在使用 THREE.js 制作一个使用 requestAnimationFrame 的 3D 游戏,并且在 Google Chrome 15 上运行速度很快。 但是,它在 Firefox 6 上真的很慢,在 IE9 上真的很慢(比 Firefox 慢)。 这确实是一个问题,我想知道是否有解决方案。

谢谢。

【问题讨论】:

解决这个问题的方法是让你从回调中运行的任何代码运行得更快。怎么做到的,不看代码就说不出来了…… 【参考方案1】:

常见的做法是创建一个 deltaTime (dt) 变量,然后将其用作每个动画/更新周期的参数。

代码仅用于可视化问题/解决方案。

// ...
timer: function()
    var now = new Date().getTime(); // get current time
    this.controls.dt = now - this.controls.time; // calculate time since last call
    this.controls.time = now; // update the current application time
    this.controls.frame++; // also we have a new frame
    return this.controls.dt ;

对于渲染函数的任何调用,然后传递 dt

// we call the update function with every request frame
update: function()
    var dt = this.timer();
    _.each(this.activeViews, function(item) item.update(dt); );  // this is underscore.js syntax

item.update(dt) 是这样的

//...
var x = this.position.get(x);
x = x + (10*dt); // meaning: x increases 10 units every ms.
this.position.x = x;

【讨论】:

【参考方案2】:

据我所知,除了减少代码占用的资源之外,没有办法真正解决这个问题。

Chrome 似乎是最快的浏览器,但通常 FF 也不甘落后,但 IE 仍然很慢。根据渲染方法(canvas、svg 或 webGL),它也非常依赖于您的本地硬件,因为它使用客户端处理大多数事情,而复杂的 webGL 渲染需要强大的 GPU 来实现良好的帧速率。

有多种方法可以实时测量帧速率,并相应地更改您的动画。 这是一个测量帧率的非常简单的示例。

function step(timestamp) 
    var time2 = new Date;
    var fps   = 1000 / (time2 - time);
    time = time2;
	
    document.getElementById('test').innerhtml = fps;
    window.requestAnimationFrame(step);


var time = new Date(), i = 0;
window.requestAnimationFrame(step);
<div id="test"></div>

这只是一个不那么准确的即时测量,您可能需要在一段时间内测量的东西,以便为正在使用的浏览器获得更正确的帧速率。

另请注意timestamp 参数,它在requestAnimationFrame 中是高分辨率时间戳,最小精度为 1 毫秒,也可用于确定动画速度和任何浏览器延迟。

【讨论】:

IE9+ 实际上相当快。根据我的经验,有时比 FF 更快。【参考方案3】:

在某些浏览器上 requestAnimationFrame 的工作方式类似于

setTimeout(callback, 1000 / (16 + N)

其中 N 是代码执行所需的时间。这意味着它将您的 FPS 上限设置为 62Hz,但如果您的代码运行缓慢,它的上限会更低。它基本上试图在每个间隙之间留出 16 毫秒的间隙。当然,这并非适用于所有浏览器,并且将来可能会发生变化,但它仍然可以让您了解它是如何工作的。

即使它在每个浏览器中实现相同,也有许多因素会影响代码的性能等。您永远无法确定您的代码将以恒定的频率运行。

【讨论】:

【参考方案4】:

Crafty 框架做了一些不同的事情,但可能适用于某些情况——每次抽签的游戏滴答数不是恒定的。相反,它会注意到帧速率何时落后于某个理想目标,并会在执行绘制步骤之前循环通过多个游戏节拍。你可以在github上看到step function。

只要游戏能够顺利运行,它就可以很好地工作。但是如果你尝试更多处理器密集型的东西,它可能会加剧这种情况,因为它会优先考虑游戏逻辑而不是动画。

无论如何,它只有在游戏逻辑和渲染逻辑在某种程度上解耦时才会起作用。 (如果它们完全解耦,您也许可以将它们放在完全独立的循环中。)

【讨论】:

【参考方案5】:

正如adeneo 提到的,requestAnimationFrame 回调被发送一个时间戳参数。这是使用该时间戳参数测量requestAnimationFrame 事件之间的增量的解决方案,而不是使用Date() 函数创建单独的变量(无论如何performance.now() 可能是更好的解决方案)。

此解决方案还包括一个启动/停止选项,以说明为什么我在每次启动时使用单独的函数来初始化 previousTimestamp,以及我为什么要设置 reqID 值。

var reqID, previousTimestamp, output;

const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

const caf = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

// This is run first to set the previousTimestamp variable with an initial value, and then call the rafLoop function.
const startStop = () => 
    if ($('#start-stop').prop('checked')) 
        reqID = raf(timestamp => 
            previousTimestamp = timestamp;
            reqID = raf(rafLoop);
        );
    
    else caf(reqID);
;

const rafLoop = timestamp => 
    animation(timestamp - previousTimestamp);
    previousTimestamp = timestamp;
    reqID = raf(rafLoop);
;

// Create animation function based on delta timestamp between animation frames
const animation = millisesonds => 
    output.html(millisesonds);
;

$(document).ready(() => 
    output = $('#output');
    $('#start-stop').change(startStop);
    $('#start-stop').prop('checked', true).trigger('change');
);
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>requestAnimationFrame</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
    <label for="start-stop">Start/Stop: </label><input class="switch" type="checkbox" id="start-stop"><br>
    <div id="output"></div>
</body>

另请参阅 https://codepen.io/sassano/pen/wvgxxMp 以获取另一个包含此 sn-p 派生的动画的示例。

【讨论】:

以上是关于如何在不同浏览器上解决requestAnimationFrame中不同的FPS?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不同的桌面浏览器上加载不同的视图

200分求如何解决CSS浏览器兼容的问题

如何解决 IE下 上传文件的兼容性问题

解决uploadify插件不同浏览器下的兼容性问题

网页中pdf如何下载

为啥我的 CSS 在移动设备上与在狭窄的桌面浏览器上看起来不同,我该如何更改? [复制]