【问题标题】:requestAnimationFrame Fixed DurationrequestAnimationFrame 固定持续时间
【发布时间】:2015-07-04 08:22:33
【问题描述】:

我正在尝试了解有关 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++);
}
<p id="counter"></p>
<p id="time"></p>

【问题讨论】:

  • 使用setInterval 代替您自己的时间。

标签: javascript animation


【解决方案1】:

setInterval 不是正确的解决方案。

您需要以不同的方式处理此问题,因为您的动画需要知道它们在动画中走了多远,以及它们开始的时间。当下一个动画帧从浏览器出现时,您可以通过它们移动的速度乘以自上一帧以来的时间来更新正在动画的任何内容的位置。正如您所说,如果您进行长时间处理,您无法保证 60fps。

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

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++);
}
<p id="counter"></p>
<p id="time"></p>

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

【讨论】:

  • 如果请求的时间小于 frameRate,它会变慢并导致渲染问题。但他只想每 1 秒渲染一次,这已经足够了。我认为使用setInterval 没有任何问题。
  • 不,他希望它持续总共 1 秒
  • 感谢您的想法。诀窍是在时间过去后退出。或者,应该设置计数器,而不是递增。此外,您可以利用回调函数的时间戳参数。请参阅下面我自己的解决方案。
【解决方案2】:

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

var myInterval = setInterval(doit,1000);

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

clearInterval(myInterval);

如果你还想利用 requestAnimationFrame 的优势,可以和 setInterval 结合使用

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

虽然我不确定如果您正在访问另一个选项卡并且 setInterval 函数将开始排列所有这些 rAF,上述内容将如何发挥作用,但请注意。

【讨论】:

  • 除了cancelInterval,它应该是clearInterval。这个答案是正确的! @down-voter
【解决方案3】:

这是解决问题的方法。诀窍是在设置的持续时间过去后退出。

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

另一个技巧是利用回调的时间戳参数。

谢谢!

/*	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>

【讨论】:

    猜你喜欢
    • 2023-01-12
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 2017-07-31
    • 2020-09-24
    • 2018-05-06
    • 2019-01-28
    相关资源
    最近更新 更多