如何解决在不同浏览器中使用`requestAnimationFrame`时出现的不同帧率问题?我正在使用`THREE.js`开发一个3D游戏,其中使用了`requestAnimationFrame`函数,在Google Chrome 15上非常快。但是在Firefox 6上很慢,在IE9上更慢(比Firefox还要慢)。这是一个非常大的问题,我想知道是否有解决方案。谢谢。
通常做法是创建一个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;
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毫秒,也可用于确定动画速度和任何浏览器延迟。
在某些浏览器中,requestAnimationFrame的工作方式类似于
setTimeout(callback, 1000 / (16 + N)
其中N是您的代码执行所需的时间。这意味着它将您的FPS限制在62Hz,但如果您的代码运行缓慢,则会将其限制在更低的值。它基本上尝试在每个间隔之间产生16ms的间隔。当然,这对于所有浏览器来说都不是真实的,并且将来可能会发生变化,但它仍然可以让您了解它的工作原理。
即使在每个浏览器中都实现相同,也有许多因素会影响您的代码性能等。您永远无法确定您的代码将以恒定的频率运行。
requestAnimationFrame
回调函数会传递一个时间戳参数。以下是一种使用该时间戳参数来测量requestAnimationFrame
事件之间差异的解决方案,而不是使用Date()
函数创建一个单独的变量(performance.now()
可能是更好的解决方案)。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,这是一个带有动画效果的示例,这段代码就是从那里衍生出来的。