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

发布于 2024-12-08 17:36:36 字数 296 浏览 0 评论 0原文

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

谢谢。

How to solve different FPS in requestAnimationFrame on different browsers?
I am making a 3D game using THREE.js that uses requestAnimationFrame and it is fast on Google Chrome 15.
However, it is really slow on Firefox 6 and really really slow (slower than Firefox) on IE9.
This is really a big problem and I am wondering if there is a solution to that.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

oО清风挽发oО 2024-12-15 17:36:37

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

只要游戏能够顺利运行,这种方法就很有效。但如果你尝试一些处理器密集型的东西,它可能会加剧这种情况,因为它会优先考虑游戏逻辑而不是动画。

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

The Crafty framework does something that's a bit different, but might work for some cases -- the number of game ticks per draws is not constant. Rather, it notices when the framerate is falling behind some ideal target, and will cycle through multiple game ticks before performing the draw step. You can see the step function on github.

This works well so long as the game would be running smoothly. But if you try something more processor intensive, it can tend to exacerbate the situation, as it will prioritize game logic over animation.

In any case, it'll only work if the game logic and render logic are somewhat decoupled. (If they were completely decoupled you might be able to put them in completely separate loops.)

翻身的咸鱼 2024-12-15 17:36:37

正如 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 了解另一个带有动画的示例这段代码就是从中衍生出来的。

As adeneo mentioned, the requestAnimationFrame callback is sent a timestamp argument. Here is a solution to measure the delta between requestAnimationFrame events using that timestamp argument instead of creating a separate variable using the Date() function (which performance.now() may be a better solution anyhow).

This solution also includes a Start/Stop option to show why I am using a separate function to initialize the previousTimestamp at each start, and why I am setting a reqID value.

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>

See also https://codepen.io/sassano/pen/wvgxxMp for another sample with animation from which this snippet was derived.

扛刀软妹 2024-12-15 17:36:36

常见的做法是创建一个 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;

The common thing to do is to create a deltaTime (dt) variable which is then be used as a parameter for every animation/update cycle.

Code is only for visualizing the problem/solution.

// ...
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 ;
}

for any call to the render function you then pass 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) looks like that

//...
var x = this.position.get(x);
x = x + (10*dt); // meaning: x increases 10 units every ms.
this.position.x = x;
失去的东西太少 2024-12-15 17:36:36

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

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 毫秒的高分辨率时间戳,也可用于确定动画的速度,并且任何浏览器延迟。

As far as I know there's no way to really fix this, other than making your code less resource intensive.

Chrome seems to be the fastest browser, but usually FF is not far behind, but IE is still slow. Depending on the rendering methods, canvas, svg or webGL, it's also very dependent on your local hardware as it uses the clientside for most things, and complicated webGL renderings need a powerful GPU to achieve good framerates.

There are ways to measure the framerate on the fly, and change your animations accordingly.
Here's a very simple example that measures framerate.

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>

This is just an instant measure that's not that accurate, you'd probably want something that measures over some time to get a more correct framerate for the browser being used.

Also note the timestamp argument, which in requestAnimationFrame is high-res timestamp with a minimal precision of 1 milliseconds, that can also be used to deterine the speed of the animation, and any browser lag.

荒人说梦 2024-12-15 17:36:36

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

setTimeout(callback, 1000 / (16 + N)

,其中 N 是代码执行所需的时间。这意味着它会将 FPS 限制在 62Hz,但如果您的代码运行缓慢,它会限制在更低的范围内。当然,这并不适用于所有浏览器,并且将来可能会改变,但它仍然可以给你一个想法。它是如何工作的。

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

On some browsers requestAnimationFrame works something like

setTimeout(callback, 1000 / (16 + N)

where N is time required for your code to execute. Which means it caps your FPS at 62Hz but if your code works slowly, it will cap at something way lower. It basically tries to make a 16ms gap between every gap. Of course, this is not true for all browsers and will probably change in the future anyway but it still may give you an idea how it works.

Even if it was implemented the same in every browser, there are many factors which affect the performance of your code and etc. You can never be sure your code will be running at a constant frequency.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文