我有setInterval
每秒30次运行一段代码。这很好用,但是当我选择另一个选项卡(以便我的代码的选项卡变为非活动状态)时,setInterval
由于某种原因被设置为空闲状态。
我制作了这个简化的测试用例(http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
如果您运行此代码然后切换到另一个选项卡,请等待几秒钟然后返回,动画会在切换到另一个选项卡时继续。因此,如果选项卡处于非活动状态,则动画不会每秒运行30次。这可以通过计算每秒调用setInterval
函数的次数来确认 - 如果选项卡处于非活动状态,则不会是30,而只是1或2。
我想这是通过设计来完成的,以便提高性能,但有没有办法禁用这种行为?这在我的场景中实际上是一个缺点。
答案 0 :(得分:139)
在大多数浏览器中,非活动选项卡的执行优先级较低,这会影响JavaScript计时器。
如果转换的值是使用帧之间的实时时间计算的,而是在每个时间间隔上固定增量,那么您不仅可以解决此问题,还可以使用{{3}来实现更好的动画效果如果处理器不是很忙,它可以达到60fps。
以下是使用requestAnimationFrame
的动画属性转换的vanilla JavaScript示例:
var target = document.querySelector('div#target')
var startedAt, duration = 3000
var domain = [-100, window.innerWidth]
var range = domain[1] - domain[0]
function start() {
startedAt = Date.now()
updateTarget(0)
requestAnimationFrame(update)
}
function update() {
let elapsedTime = Date.now() - startedAt
// playback is a value between 0 and 1
// being 0 the start of the animation and 1 its end
let playback = elapsedTime / duration
updateTarget(playback)
if (playback > 0 && playback < 1) {
// Queue the next frame
requestAnimationFrame(update)
} else {
// Wait for a while and restart the animation
setTimeout(start, duration/10)
}
}
function updateTarget(playback) {
// Uncomment the line below to reverse the animation
// playback = 1 - playback
// Update the target properties based on the playback position
let position = domain[0] + (playback * range)
target.style.left = position + 'px'
target.style.top = position + 'px'
target.style.transform = 'scale(' + playback * 3 + ')'
}
start()
body {
overflow: hidden;
}
div {
position: absolute;
white-space: nowrap;
}
<div id="target">...HERE WE GO</div>
@UpTheCreek评论:
很好的演示问题,但仍然 有一些事情你需要继续运行。
如果您有所需的后台任务以给定的时间间隔精确执行,则可以使用requestAnimationFrame。请查看HTML5 Web Workers了解更多详情......
使用CSS过渡/动画而不是基于JavaScript的动画可以避免此问题和Möhre's answer below,从而增加了相当大的开销。我推荐这个many others让你从CSS转换中受益,就像animate()
方法一样。
答案 1 :(得分:66)
我遇到了与音频褪色和HTML5播放器相同的问题。当标签变为不活动时,它被卡住了。 所以我发现WebWorker可以无限制地使用间隔/超时。我用它来向主javascript发布“ticks”。
WebWorkers代码:
var fading = false;
var interval;
self.addEventListener('message', function(e){
switch (e.data) {
case 'start':
if (!fading){
fading = true;
interval = setInterval(function(){
self.postMessage('tick');
}, 50);
}
break;
case 'stop':
clearInterval(interval);
fading = false;
break;
};
}, false);
主要Javascript:
var player = new Audio();
player.fader = new Worker('js/fader.js');
player.faderPosition = 0.0;
player.faderTargetVolume = 1.0;
player.faderCallback = function(){};
player.fadeTo = function(volume, func){
console.log('fadeTo called');
if (func) this.faderCallback = func;
this.faderTargetVolume = volume;
this.fader.postMessage('start');
}
player.fader.addEventListener('message', function(e){
console.log('fader tick');
if (player.faderTargetVolume > player.volume){
player.faderPosition -= 0.02;
} else {
player.faderPosition += 0.02;
}
var newVolume = Math.pow(player.faderPosition - 1, 2);
if (newVolume > 0.999){
player.volume = newVolume = 1.0;
player.fader.postMessage('stop');
player.faderCallback();
} else if (newVolume < 0.001) {
player.volume = newVolume = 0.0;
player.fader.postMessage('stop');
player.faderCallback();
} else {
player.volume = newVolume;
}
});
答案 2 :(得分:33)
有一个使用Web Workers的解决方案(如前所述),因为它们在单独的进程中运行而且没有放慢速度
我编写了一个可以在不更改代码的情况下使用的小脚本 - 它只是覆盖了函数setTimeout,clearTimeout,setInterval,clearInterval。
只需在所有代码之前加入它。
答案 3 :(得分:13)
这样做:
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.stop(true,true).css("left", a);
}, 1000 / 30);
非活动浏览器标签缓冲了部分setInterval
或setTimeout
功能。
stop(true,true)
将停止所有缓冲的事件,并立即执行最后一个动画。
window.setTimeout()
方法现在限制在非活动选项卡中每秒发送不超过一次超时。此外,它现在将嵌套超时限制为HTML5规范允许的最小值:4 ms(而不是用于钳位的10 ms)。
答案 4 :(得分:5)
我认为在此示例中对此问题有最好的理解:http://jsfiddle.net/TAHDb/
我在做一件简单的事情:
间隔1秒,每次隐藏第一个跨度并将其移动到最后,并显示第二个跨度。
如果你留在页面上它按预期工作。 但如果你隐藏标签几秒钟,当你回来时你会看到一个令人厌烦的东西。
就像你现在不活动期间没有发生的所有事件一样,这一切都会在一次完成。所以几秒钟后你就会得到X事件。它们非常快,可以同时看到所有6个跨度。
因此,接缝镀铬只会延迟事件,所以当你回来时,所有事件都会发生,但一下子就会发生......
这是一个实际的应用程序,用于简单的幻灯片演示。想象一下这些数字是图像,如果用户在回来时隐藏了标签隐藏,他会看到所有的模仿浮动,完全被吸引。
要解决此问题,请使用像pimvdb一样的stop(true,true)。 这将清除事件队列。
答案 5 :(得分:2)
对我来说,像在这里播放其他音频一样在后台播放音频并不重要,我的问题是我有一些动画,当您在其他选项卡中又回到它们时,它们表现得很疯狂。我的解决方案是将这些动画放入 if 中,以防止无效标签:
if (!document.hidden){ //your animation code here }
感谢我的动画在
答案 6 :(得分:1)
受到Ruslan Tushov图书馆的极大影响,我创造了自己的小library。只需在<head>
中添加脚本,它就可以使用setInterval
修补setTimeout
和WebWorker
。
答案 7 :(得分:0)
这是我的粗略解决方案
RxTableViewReactiveArrayDataSource
答案 8 :(得分:0)
播放音频文件可确保暂时具有完整的后台Java脚本性能
对我来说,这是最简单且干扰最小的解决方案-除了播放微弱/几乎为空的声音外,没有其他潜在的副作用
您可以在此处找到详细信息:https://stackoverflow.com/a/51191818/914546
(从其他答案中,我看到有些人使用Audio标记的不同属性,我确实想知道是否可以使用Audio标记来发挥全部性能,而无需实际播放某些东西)
答案 9 :(得分:0)
在选项卡处于非活动状态或工作时,setInterval
和requestAnimationFrame
都不起作用,但在正确的时间段不起作用。一种解决方案是使用其他事件源。例如,网络套接字或网络工作者是两个事件源,当选项卡处于非活动状态时,它们可以正常工作。因此,无需将所有代码移至Web Worker,只需将worker用作时间事件源:
// worker.js
setInterval(function() {
postMessage('');
}, 1000 / 50);
。
var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
var t2 = new Date().getTime();
console.log('fps =', 1000 / (t2 - t1) | 0);
t1 = t2;
}
jsfiddle link此示例。
答案 10 :(得分:0)
注意:如果您希望间隔在背景上播放(例如播放音频或...),但是如果您对例如动画在以下情况下无法正常播放感到困惑,则此解决方案不适用回到您的页面(标签),这是一个很好的解决方案。
有很多方法可以实现这一目标,也许“ WebWorkers”是最标准的方法。但是当然,这不是最简单方便的方法,尤其是如果您没有足够的时间,因此您可以尝试这种方式:
►BASICCONCEPT:
1-为您的间隔(或动画)建立一个名称并设置间隔(动画),这样它将在用户首次打开您的页面时运行:var interval_id = setInterval(your_func, 3000);
2-通过$(window).focus(function() {});
和$(window).blur(function() {});
,clearInterval(interval_id)
可以在每次停用浏览器(tab)时运行,并且每次{{1 }}
►示例代码:
interval_id = setInterval();
答案 11 :(得分:0)
我认为使用jQuery 3.3.x
现在最简单的方法是使用此功能:
intervalFunction();
$([window, document]).on('focusin', function(){
if (!intervalId){
intervalFunction();
}
}).on('focusout', function(){
if (intervalId) {
clearInterval(intervalId);
intervalId = undefined;
}
});
function intervalFunction() {
// Your function hire
}
intervalFunction()
会在页面加载时立即启动,然后如果焦点又回到了您尝试重新激活间隔,但是如果页面失去焦点,则可以随时返回到此。
清晰,轻松。此外,该解决方案不使用.focus()
,.focusin()
和.focusout()
答案 12 :(得分:0)
这是一个很老的问题,但是我遇到了同样的问题。
如果您在chrome上运行网络,则可以阅读这篇文章Background Tabs in Chrome 57
。
基本上,如果间隔计时器没有用完计时器预算,则间隔计时器可以运行。
预算消耗基于计时器内任务的CPU时间使用情况。
根据我的情况,我将视频绘制到画布上并传输到WebRTC。
即使选项卡处于非活动状态,webrtc视频连接也将不断更新。
但是您必须使用setInterval
而不是requestAnimationFrame
。
不过,建议不要将其用于UI渲染。
最好听visibilityChange
事件并相应地更改渲染机制。
此外,您可以尝试@ kaan-soral,它应该基于文档有效。
答案 13 :(得分:-1)
我能够使用音频标签在最短250ms内调用我的回调函数并处理其ontimeupdate事件。它在一秒钟内被称为3-4次。它优于一秒滞后的setTimeout