requestAnimationFrame固定持续时间

时间:2015-07-03 13:19:44

标签: javascript animation

我想了解requestAnimationFrame的一件事。

在下面的示例代码中,我正在运行60次,这应该与您通常获得的典型60帧/秒相匹配,因此它应该持续1秒。但是,我故意通过调用浪费时间来拖延它。

显然,这需要更长时间(在我的机器上大约5秒钟)。

问题是,我如何设置动画以使其持续一段固定的持续时间,例如1秒 - 大概这应该在过程中跳过几帧?

虽然示例听起来具体,但它具有更广泛的背景。我想创建诸如淡入淡出或滑动的动画,其具有设定的持续时间而不是设定的帧数,因为帧速率不是完全可预测的。这将匹配CSS3动画的行为。

由于



var i=0;
var start=new Date();
var end;

var counter=document.getElementById('counter');
var time=document.getElementById('time');

doit();

function doit() {
  if(i>=60) {
    end=new Date();
    time.innerHTML=end.getTime() - start.getTime();
    return;
  }
  wasteTime();
  counter.innerHTML+=i+' ';
  i++;
  requestAnimationFrame(doit)
}

function wasteTime() {
  for(var i=0;i<100000000;i++);
}
&#13;
<p id="counter"></p>
<p id="time"></p>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:1)

setInterval不是正确的解决方案。

你需要稍微改变一下,因为你的动画需要知道它们的动画有多远,以及它们开始的时间。当下一个动画帧从浏览器中传出时,您可以通过它们移动的速度乘以它自上一帧以来的长度来更新任何动画的位置。正如你所说,如果你进行了长时间的处理,你不能保证60fps。

在您的示例中,您需要将i增加一个适合自上一帧以来经过的时间量的数字:

&#13;
&#13;
var i=0;
var start=new Date();
var last=new Date();
var end;

var counter=document.getElementById('counter');
var time=document.getElementById('time');

doit();

function doit() {
  if(i>=60) {
    end=new Date();
    time.innerHTML=end.getTime() - start.getTime();
    return;
  }
  
  //Rather than just incrementing i by 1, here we
  //look at the difference since the last frame
  //and increment i by an appropriate amount
  var diff = new Date() - last;

  counter.innerHTML+=i+' ';
  i += diff/60;

  requestAnimationFrame(doit)
  last = new Date();
  wasteTime();
}

function wasteTime() {
  for(var i=0;i<100000000;i++);
}
&#13;
<p id="counter"></p>
<p id="time"></p>
&#13;
&#13;
&#13;

注意:我不确定为什么这仍然需要超过1秒的时间来运行,仍在寻找。可能是它与浏览器交互以写出HTML。

答案 1 :(得分:0)

如果您想在循环之间使用固定的间隔,最好使用setInterval();

var myInterval = setInterval(doit,1000);

这将每秒启动doit() - 函数。要取消您的间隔,您需要

clearInterval(myInterval);

如果您仍想利用requestAnimationFrame的优点,可以将它与setInterval结合使用

var myInterval = setInterval(function () {
    requestAnimationFrame(doit);
},1000);

虽然我不确定如果您正在访问另一个标签并且setInterval-function将开始排列所有这些rAF,上述内容将会如何显示,所以请注意。

答案 2 :(得分:0)

这是问题的解决方案。诀窍是在设定的持续时间结束后退出。

下面示例中的计数器不应递增,而是设置为比例值。如果是这样,它也可以正确使用退出。

另一个技巧是利用回调的timestamp参数。

谢谢!

/*	HTML
	================================================
	<p id="counter"></p>
	<p id="time"></p>
	================================================ */


var limit=60;
var i=0;
var start=null;

var counter=document.getElementById('counter');
var time=document.getElementById('time');


doit();

function doit(timestamp) {
	if(!start) start=timestamp;
	var lapsed=timestamp-start;
	if(lapsed>=1000) {	//	if(i>=limit) {
		time.innerHTML='<br>'+lapsed;
		return;
	}
	wasteTime();
	counter.innerHTML += i+' ';
	i=lapsed/1000*limit;		//	Rather than increment
	requestAnimationFrame(doit)
}

function wasteTime() {
	for(var i=0;i<100000000;i++);
}
<p id="counter"></p>
<p id="time"></p>