多个窗口上的 60 fps 动画
99.99% 的时间,requestAnimationFrame 是制作视觉动画的方式。这是一个很棒的工具,与屏幕刷新率同步,具有很高的计时精度,并且电池友好。
这最后一个优势是你的问题:为了省电,浏览器只允许以 60fps 的速度对当前聚焦的窗口进行屏幕同步。所有其他窗口都会延迟,帧速率较低。
由于您希望拥有多个窗口,因此您只能获得一个焦点,因此只有一个以 60 fps 刷新,所有其他窗口将减慢到 5 fps 左右。
如何规避?
WebAudioAPI 确实有自己的低级和高精度时钟系统。
通过“低级”,我的意思是这个时钟系统不绑定到主 js-thread*。在某些实现(chrome)上,所有 WebAudioAPI 甚至都在并行线程上运行。对我们来说更重要的是,这个时钟系统不仅与聚焦窗口相关。这确实意味着我们可以在后台窗口中以 60fps 的速度运行代码。
这是一个基于 WebAudioAPI 时钟的定时循环的简单实现。
(*请注意,虽然时钟不绑定到主 js 线程,但事件处理程序是)。
/*
An alternative timing loop, based on AudioContext's clock
@arg callback : a callback function
with the audioContext's currentTime passed as unique argument
@arg frequency : float in ms;
@returns : a stop function
*/
function audioTimerLoop(callback, frequency) {
var freq = frequency / 1000; // AudioContext time parameters are in seconds
var aCtx = new AudioContext();
// Chrome needs our oscillator node to be attached to the destination
// So we create a silent Gain Node
var silence = aCtx.createGain();
silence.gain.value = 0;
silence.connect(aCtx.destination);
onOSCend();
var stopped = false; // A flag to know when we'll stop the loop
function onOSCend() {
var osc = aCtx.createOscillator();
osc.onended = onOSCend; // so we can loop
osc.connect(silence);
osc.start(0); // start it now
osc.stop(aCtx.currentTime + freq); // stop it next frame
callback(aCtx.currentTime); // one frame is done
if (stopped) { // user broke the loop
osc.onended = function() {
aCtx.close(); // clear the audioContext
return;
};
}
};
// return a function to stop our loop
return function() {
stopped = true;
};
}
然后这样称呼它:
var stop_anim = audioTimerLoop(yourCallback, 60); // runs 'yourCallback' every 60ms
然后停止它:
stop_anim();
好的,现在我们可以在模糊的窗口上运行流畅的动画了。
但我想在 16 个窗口上运行它!
不幸的是,浏览器在创建 AudioContext 时会受到硬件限制。 (例如,在我的计算机上,我不能同时运行超过 6 个上下文。)
这里的解决方案是在一个主窗口上运行所有代码。
从此主窗口,您将首先
- 打开所有其他窗口,以便您可以访问它们的内容,
- 获取画布元素的上下文,
- 利用这些背景
这样,您在主窗口上有一个更新线程,而所有其他窗口只需要渲染。
(请确保允许弹出窗口并禁用您的广告拦截器)。