Kri*_*ssy 1 javascript requestanimationframe
我认为传递的时间戳参数requestAnimationFrame计算错误(在 Chrome 和 Firefox 中测试)。
在下面的代码片段中,我有一个循环,大约需要。300ms(您可能需要调整循环迭代的次数)。计算出的值delta应始终大于打印的循环“持续时间”。奇怪的是,有时慢有时不慢。为什么?
let timeElapsed = 0;
let animationID;
const loop = timestamp => {
const delta = timestamp - timeElapsed;
timeElapsed = timestamp;
console.log('delta', delta);
// some heavy load for the frame
const start = performance.now();
let sum = 0;
for (let i = 0; i < 10000000; i++) {
sum += i ** i;
}
console.warn('duration', performance.now() - start);
animationID = requestAnimationFrame(loop)
}
animationID = requestAnimationFrame(loop);
setTimeout(() => {
cancelAnimationFrame(animationID);
}, 2000);
Run Code Online (Sandbox Code Playgroud)
jsFiddle: https: //jsfiddle.net/Kritten/ohd1ysmg/53/
请注意,该片段会在两秒后停止。
至少在 Blink 和 Gecko 中,传递给 rAF 回调的时间戳是最后一个VSync脉冲之一。
在该代码片段中,CPU 和事件循环被锁定约 300 毫秒,但显示器仍然以相同的速率并行发出 VSync 脉冲。
当浏览器完成这 300 毫秒的计算后,它必须安排一个新的动画帧。
在下一次事件循环迭代中,它将检查显示器是否发送了新的 VSync 脉冲,既然发送了(在 60Hz 上大约 18 次),它将几乎立即执行新的 rAF 回调。
因此,传递给 rAF 回调的时间戳可能确实是上次回调结束之前的时间,因为事件循环在最后一个 VSync 脉冲后被释放。
强制执行此操作的一种方法是使计算持续时间比一帧的持续时间长一点,例如在 60Hz 显示器上,VSync 脉冲每 16.67 毫秒发生一次,因此如果我们将事件循环锁定 16.7 毫秒,我们就很确定时间戳增量小于实际计算时间:
let stopped = false;
let perf_elapsed = performance.now();
let timestamp_elapsed = 0;
let computation_time = 0;
let raf_id;
const loop = timestamp => {
const perf_now = performance.now();
const timestamp_delta = +(timestamp - timestamp_elapsed).toFixed(2);
timestamp_elapsed = timestamp;
const perf_delta = +(perf_now - perf_elapsed).toFixed(2);
perf_elapsed = perf_now;
const ERROR = timestamp_delta < computation_time;
if (computation_time) {
console.log({
computation_time,
timestamp_delta,
perf_delta,
ERROR
});
}
// some heavy load for the frame
const computation_start = performance.now();
const frame_duration = 1000 / frequency.value;
const computation_duration = (Math.ceil(frame_duration * 10) + 1) / 10; // add 0.1 ms
while (performance.now() - computation_start < computation_duration) {}
computation_time = performance.now() - computation_start;
raf_id = requestAnimationFrame(loop)
}
frequency.oninput = evt => {
cancelAnimationFrame( raf_id );
console.clear();
raf_id = requestAnimationFrame(loop);
setTimeout(() => {
cancelAnimationFrame( raf_id );
}, 2000);
};
frequency.oninput();Run Code Online (Sandbox Code Playgroud)
In case your monitor has a different frame-rate than th common 60Hz, you can insert it here:
<input type="number" id="frequency" value="60" steps="0.1">Run Code Online (Sandbox Code Playgroud)
那么,在这个时间戳和你的调用之间使用什么,performance.now()我猜,时间戳告诉你帧何时开始,performance.now()会告诉你代码何时执行,如果需要,你可以使用两者。即使没有跨帧的如此大的计算,您也可以在您的任务之前安排其他需要花费几毫秒才能完成的任务,甚至是应该在之后执行的大型 CSS 组合,而您没有真正的方法知道。
| 归档时间: |
|
| 查看次数: |
442 次 |
| 最近记录: |