Javascript requestAnimationFrame的时间戳不可靠

Javascript requestAnimationFrame的时间戳不可靠,javascript,requestanimationframe,Javascript,Requestanimationframe,我认为requestAnimationFrame传递的timestamp参数计算错误(在Chrome和Firefox中测试) 在下面的代码片段中,我有一个大约需要300毫秒的循环(您可能需要调整循环迭代次数)。 计算的增量应始终大于打印的循环“持续时间”。 奇怪的是,有时速度较慢,有时则不然。为什么? let timeappeased=0; 让animationID; 常量循环=时间戳=>{ const delta=时间戳-时间流逝; 时间流逝=时间戳; console.log('delta',

我认为
requestAnimationFrame
传递的timestamp参数计算错误(在Chrome和Firefox中测试)

在下面的代码片段中,我有一个大约需要300毫秒的循环(您可能需要调整循环迭代次数)。 计算的增量应始终大于打印的循环“持续时间”。 奇怪的是,有时速度较慢,有时则不然。为什么?

let timeappeased=0;
让animationID;
常量循环=时间戳=>{
const delta=时间戳-时间流逝;
时间流逝=时间戳;
console.log('delta',delta);
//车架上有些重负载
const start=performance.now();
设和=0;
对于(设i=0;i<10000000;i++){
总和+=i**i;
}
console.warn('duration',performance.now()-start);
animationID=requestAnimationFrame(循环)
}
animationID=请求AnimationFrame(循环);
设置超时(()=>{
取消AnimationFrame(animationID);
}, 2000);
jsFiddle:


请不要让代码段在两秒钟后停止。

至少在Blink和Gecko中,传递给rAF回调的时间戳是最后一个脉冲之一

在代码片段中,CPU和事件循环被锁定了大约300ms,但是监视器仍然以相同的速率并行地发出其VSync脉冲

当浏览器完成这300毫秒的计算时,它必须安排一个新的动画帧。
在下一次事件循环迭代中,它将检查监视器是否发送了新的VSync脉冲,并且由于它发送了新的VSync脉冲(在60Hz上大约18次),它将几乎立即执行新的rAF回调

因此,传递给rAF回调的时间戳可能确实是上次回调结束之前的一个时间戳,因为事件循环在最后一个VSync脉冲后被释放

强制执行此操作的一种方法是使您的计算持续时间略大于一帧的持续时间,例如,在60Hz监视器上,VSync脉冲将每16.67ms发生一次,因此,如果我们将事件循环锁定16.7ms,则时间戳增量肯定会小于实际计算时间:

let stopped=false;
让perf_appeased=performance.now();
让时间戳_过去=0;
设计算时间=0;
让raf_识别;
常量循环=时间戳=>{
const perf_now=性能。now();
constamp timestamp_delta=+(timestamp-timestamp_已过);
timestamp_appeased=时间戳;
const perf_delta=+(perf_now-perf_appeased);
perf_appeased=perf_now;
常量错误=时间戳_delta<计算时间;
if(计算时间){
console.log({
计算时间,
时间戳_delta,
性能三角洲,
错误
});
}
//车架上有些重负载
常量计算_start=performance.now();
const frame_duration=1000/frequency.value;
常量计算持续时间=(Math.ceil(frame\u duration*10)+1)/10;//添加0.1毫秒
while(performance.now()-computation\u start{
取消动画帧(raf_id);
console.clear();
raf_id=请求动画帧(循环);
设置超时(()=>{
取消动画帧(raf_id);
}, 2000);
};
oninput()的频率
如果您的显示器的帧速率与常见的60Hz不同,您可以将其插入此处:

至少在Blink和Gecko中,传递给rAF回调的时间戳是最后一个脉冲之一

在代码片段中,CPU和事件循环被锁定了大约300ms,但是监视器仍然以相同的速率并行地发出其VSync脉冲

当浏览器完成这300毫秒的计算时,它必须安排一个新的动画帧。
在下一次事件循环迭代中,它将检查监视器是否发送了新的VSync脉冲,并且由于它发送了新的VSync脉冲(在60Hz上大约18次),它将几乎立即执行新的rAF回调

因此,传递给rAF回调的时间戳可能确实是上次回调结束之前的一个时间戳,因为事件循环在最后一个VSync脉冲后被释放

强制执行此操作的一种方法是使您的计算持续时间略大于一帧的持续时间,例如,在60Hz监视器上,VSync脉冲将每16.67ms发生一次,因此,如果我们将事件循环锁定16.7ms,则时间戳增量肯定会小于实际计算时间:

let stopped=false;
让perf_appeased=performance.now();
让时间戳_过去=0;
设计算时间=0;
让raf_识别;
常量循环=时间戳=>{
const perf_now=性能。now();
constamp timestamp_delta=+(timestamp-timestamp_已过);
timestamp_appeased=时间戳;
const perf_delta=+(perf_now-perf_appeased);
perf_appeased=perf_now;
常量错误=时间戳_delta<计算时间;
if(计算时间){
console.log({
计算时间,
时间戳_delta,
性能三角洲,
错误
});
}
//车架上有些重负载
常量计算_start=performance.now();
const frame_duration=1000/frequency.value;
常量计算持续时间=(Math.ceil(frame\u duration*10)+1)/10;//添加0.1毫秒
while(performance.now()-computation\u start{
取消动画帧(raf_id);
console.clear();
raf_id=请求动画帧(循环);
设置超时(()=>{
取消动画帧(raf_id);
}, 2000);
};
oninput()的频率
如果您的显示器的帧速率与常见的60Hz不同,您可以将其插入此处: