当 Chrome 中的标签处于非活动状态时,如何使 setInterval 也起作用?

新手上路,请多包涵

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

我猜想这是为了提高系统性能而设计的,但是有什么办法可以禁用这种行为吗?

在我的场景中,这实际上是一个缺点。

原文由 pimvdb 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

在大多数浏览器上,非活动选项卡的执行优先级较低,这会影响 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 与 JS“动画”

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

原文由 kbtz 发布,翻译遵循 CC BY-SA 4.0 许可协议

我在音频衰减和 HTML5 播放器方面遇到了同样的问题。当标签变得不活跃时,它被卡住了。所以我发现 WebWorker 可以不受限制地使用间隔/超时。我用它来发布“ticks”到主 javascript。

网络工作者代码:

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

原文由 Möhre 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏