如何在不同浏览器的requestAnimationFrame
中解决不同的FPS?
我正在使用使用THREE.js
的{{1}}制作3D游戏,并且 Google Chrome 15 的速度很快。
但是,它在 Firefox 6 上确实很慢,并且在 IE9 上真的很慢(比Firefox慢)。
这确实是一个大问题,我想知道是否有解决方案。
感谢。
答案 0 :(得分:14)
常见的事情是创建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;
答案 1 :(得分:5)
据我所知,没有办法真正解决这个问题,除了让您的代码减少资源密集度。
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毫秒,也可用于确定动画的速度,以及任何浏览器滞后
答案 2 :(得分:3)
Crafty框架做了一些不同的事情,但可能适用于某些情况 - 每次抽奖的游戏滴答数不是恒定的。相反,它注意到当帧速率落在某个理想目标之后,并且在执行绘制步骤之前将循环通过多个游戏滴答。你可以在github上看到step function。
只要游戏运行顺畅,这种方法就可以正常运行。但是如果你尝试更多处理器密集型的东西,它可能会加剧这种情况,因为它会优先考虑游戏逻辑而不是动画。
在任何情况下,它只有在游戏逻辑和渲染逻辑稍微分离时才有效。 (如果它们完全分离,您可以将它们放在完全独立的循环中。)
答案 3 :(得分:2)
在某些浏览器上,requestAnimationFrame的工作方式类似于
setTimeout(callback, 1000 / (16 + N)
其中N是代码执行所需的时间。这意味着它会将你的FPS限制在62Hz,但是如果你的代码工作缓慢,它的上限会低一些。它基本上试图在每个间隙之间产生16ms的差距。当然,对于所有浏览器来说都不是这样,并且未来可能会改变,但它仍然可以让你知道它是如何工作的。
即使在每个浏览器中都实现了相同的功能,但仍有许多因素会影响代码的性能等。您永远无法确定代码是否会以恒定频率运行。
答案 4 :(得分:0)
正如 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 以获取此代码段源自的带有动画的另一个示例。