JavaScript:如何获取window.requestAnimationFrame之间的时间差

11

如何在JavaScript中获取“window.requestAnimationFrame”回调之间的时间差?

我已经尝试过:

// create the best .requestAnimationFrame callback for each browser
window.FPS = (function() {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {window.setTimeout(callback, 1000 / 60);};
})();

// start animation loop
var dt, stamp = (new Date()).getTime();
function loop() {
    window.FPS(loop);
    var now = (new Date()).getTime();
    var dt = now - stamp;
    stamp = now;
}
// has "dt" the best accuracy?

1
潜在的重复问题:https://dev59.com/NWsy5IYBdhLWcg3w2Bk6 - Norman Breau
相当确定回调函数只传递一个参数,即时间戳(在完整的本地requestAnimationFrame实现中),如果您正在寻找对polyfill的支持,则有一些可以模拟时间戳参数比您的window.FPS polyfill更好的。这个 或许是我见过的最好的之一。与 SO问题相关。 - OJay
1
顺便说一下,你可以完全省略那个IEFE和它的return,只需分配window.raf = … || … || function(cb){…};即可。 - Bergi
4个回答

6

现代大多数浏览器会自动将高精度时间戳作为参数传递到每个requestAnimation回调循环中:http://caniuse.com/#search=performance

因此,您只需从当前时间戳减去上一个时间戳,即可得到自上次运行循环以来经过的时间。

以下是示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var startingTime;
var lastTime;
var totalElapsedTime;
var elapsedSinceLastLoop;

var $total=$('#total');
var $loop=$('#loop');

requestAnimationFrame(loop);

function loop(currentTime){
  if(!startingTime){startingTime=currentTime;}
  if(!lastTime){lastTime=currentTime;}
  totalElapsedTime=(currentTime-startingTime);
  elapsedSinceLastLoop=(currentTime-lastTime);
  lastTime=currentTime;
  $total.text('Since start: '+totalElapsedTime+' ms');
  $loop.text('Since last loop: '+elapsedSinceLastLoop+' ms');
  requestAnimationFrame(loop);
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p id=total>T1</p>
<p id=loop>T2</p>
<canvas id="canvas" width=300 height=300></canvas>

对于那些不支持Performance的浏览器,您需要在循环内使用Date.now()代替currentTime,因为这些浏览器没有自动将时间戳发送到循环中。


那个parseInt在那里做什么?作为一个js金徽章持有者,你应该知道得更好! - Bergi
@Bergi。哈哈,同意! - markE

5

"dt"有最高的准确度吗?

不是。根据文档

回调方法被传递一个参数,即DOMHighResTimeStamp,该参数指示由requestAnimationFrame排队的回调开始触发时的当前时间。

因此,您应该使用它来获得高精度。

function loop(now) {
    var last = now || Date.now(); // fallback if no precise time is given
    window.FPS(function(now) {
        now = now || Date.now();
        var dt = now - last;
        if (dt != 0) // something might be wrong with our frames
             console.log(dt);
        loop(now);
    });
}
window.FPS(loop);

(jsfiddle演示)


值得注意的是,rAF时间戳无论如何都会被量化为16.67毫秒、16.67x2等,因为它与显示器刷新率同步。 - user1693593
var last = now || Date.now() 是错误的,我认为。"now" 是自动执行开始后的时间戳,而 Date.now() 是 Unix 时间戳。 - marirena
@marirena:不,根据文档,now表示的是当前时间,而不是自rAF调用以来的时间跨度。它是一个绝对的毫秒级时间戳,就像Date.now一样,但精度更高(可能有不同的起点)。你也可以完全省略|| …部分,这只是为了兼容不兼容的rAF shims而设置的备用方案。 - Bergi
我刚刚使用console.log(now)运行了它,“now”在开始时读取为0,并且每秒增加1000。 - marirena
@marirena:就像我说的一样。它可能起源于页面加载而不是1970年1月1日,但它仍然是一个毫秒级时间戳。由于我们只关心增量,所以起源并不重要。 - Bergi

2

我将为想要使用这种模式的每个人撰写一个清晰的结论

// CREATING AN FPS ENGINE

window.FPS = (function() {
    return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback) {window.setTimeout(callback, 1000 / 60);};
})();

var FPS = {
    loop: function(canvas_object) { // OPTIONAL canvas_object, I think it increases performance | canvas_object = document.getElementById("canvas_id")
        var ticks = window.FPS(function(now){
            var dt = now - FPS.stamp || 0;
            FPS.stamp = now;
            FPS.update(dt, FPS.stamp, ticks);
            FPS.loop(canvas_object);
        }, canvas_object);

    },
    update: undefined,
    stamp: undefined
};

// USING THE FPS ENGINE

FPS.loop(the_canvas_object); // starts the engine
FPS.update = function(dt, stamp, ticks) {
    // The game/video loop, using accurate dt. Stamp is the time since engine started. Ticks is the number of the loop cycles
    console.log("dt: " + dt + ", Stamp: " + stamp + ", Ticks: " + ticks); // check output   
    // HAPPY GAME CREATING
    var fps= (1 / (dt / 1000)).toFixed(1);
};

2
(1 / (dt / 1000)) 可以简化为 (1000 / dt)。不知道为什么你这样做。 - Al.G.

0
your_module.prototype.log_frame_speed=function(){
    this.cframe++;
    if(this.cframe>10000)  {sys.exit();}

    this.date_now=Date.now();
    this.date_delta=this.date_now-this.date_previous;
    this.date_previous=this.date_now;
    //console.log(this.date_delta);
    };  

your_module.prototype.implement_next_frame=function(){
    // code update.                                                                                                 
    // render update.
    this.log_frame_speed();

    requestAnimationFrame(this.implement_next_frame.bind(this));
    }; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接