我有一个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。

我想这样做是为了提高系统性能,但是有什么方法可以禁用这种行为吗?

这在我看来是劣势。


当前回答

setInterval和requestAnimationFrame都不工作时,选项卡是不活跃的或工作,但不是在正确的时间。一种解决方案是使用另一个时间事件源。例如,web套接字或web工作者是两个事件源,当选项卡是非活动的时候工作得很好。所以不需要把你所有的代码都移动到一个web worker,只需要使用worker作为一个time事件源:

// 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链接。

其他回答

我在使用HTML5播放器时也遇到了同样的问题。当标签变成非活动时,它被卡住了。 所以我发现WebWorker允许使用没有限制的间隔/超时。我用它来张贴“tick”到主javascript。

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;
    }
});

有一个解决这个问题的方法,尽管实际上标签必须在某些窗口中被激活。

让你的非活动标签成为一个单独的浏览器窗口。 不要让任何其他窗口最大化(除非最大化的窗口在你的后面)。

这应该给浏览器一个始终处于活动状态的印象。

这有点麻烦,但也是一个快速的胜利。只要你能控制窗户的排列。

在大多数浏览器上,不活跃的选项卡执行优先级低,这可能会影响JavaScript计时器。

如果你的过渡值是使用帧间的实时时间间隔来计算的,而不是每个间隔上的固定增量,你不仅可以解决这个问题,而且还可以通过使用requestAnimationFrame来实现屏蔽动画,因为如果处理器不是很忙的话,它可以达到60fps。

下面是一个使用requestAnimationFrame实现动画属性转换的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>


对于后台任务(与ui无关)

@UpTheCreek评论:

表现问题没问题,但还是 有些事情你需要继续做下去。

如果你有后台任务需要在给定的时间间隔内精确执行,你可以使用HTML5 Web Workers。看看下面Möhre的回答,了解更多细节…

CSS vs JS的“动画”

这个问题和其他许多问题都可以通过使用CSS转换/动画来避免,而不是基于JavaScript的动画,这会增加相当大的开销。我推荐这个jQuery插件,它可以让你像animate()方法一样从CSS过渡中受益。

对我来说,像其他人一样在后台播放音频并不重要,我的问题是我有一些动画,当你在其他选项卡中回到它们时,它们就像疯了一样。我的解决方案是把这些动画放在里面,如果这是防止非活动标签:

if (!document.hidden){ //your animation code here }

多亏了这个,我的动画只有在TAB激活时才会运行。 我希望这能对我的案子有所帮助。

我认为最好的理解这个问题的例子是:http://jsfiddle.net/TAHDb/

我在这里做了一件简单的事情:

间隔1秒,每次隐藏第一个跨度,并将其移动到最后,并显示第二个跨度。

如果你停留在页面上,它就像它所设想的那样工作。 但如果你把标签隐藏了几秒钟,当你回来的时候,你会看到一个奇怪的东西。

这就像所有在你现在不活跃的时候没有发生的事情都会在一次发生一样。所以在几秒钟内你会看到X个事件。它们是如此之快,以至于有可能同时看到所有6个跨度。

所以它接缝铬只延迟事件,所以当你回来所有的事件将发生,但所有的一次…

这是一个简单的幻灯片的实际应用。想象数字是图像,如果用户保持标签隐藏,当他回来的时候,他会看到所有的imgs浮动,完全混乱。

要解决这个问题,请像pimvdb告诉的那样使用stop(true,true)。 这将清除事件队列。