切换标签窗口后js定时器自动停止了,如何在激活标签后又继续呢?

JavaScript 定时器在标签页失去焦点(例如切换到其他标签页或最小化浏览器)时,会被浏览器降低优先级或暂停,以节省资源。这会导致定时器不准确,甚至看起来停止了。要解决这个问题,你需要使用 requestAnimationFrame 或手动调整时间差。

1. 使用 requestAnimationFrame (推荐)

requestAnimationFrame 是浏览器提供的 API,它会在浏览器下次重绘之前执行回调函数。这比 setTimeoutsetInterval 更高效,也更能适应浏览器的节能机制。当标签页失去焦点时,requestAnimationFrame 会暂停,重新获得焦点时会继续执行。

let startTime = null;
let animationId;

function animate(timestamp) {
  if (!startTime) startTime = timestamp;
  const progress = timestamp - startTime;

  // 执行你的动画或定时器逻辑,例如更新进度条
  // ...

  animationId = requestAnimationFrame(animate);
}

// 启动动画
animationId = requestAnimationFrame(animate);

// 可选:在标签页失去焦点时暂停动画,重新获得焦点时继续
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    startTime = null; // 重置起始时间
    animationId = requestAnimationFrame(animate);
  } else {
    cancelAnimationFrame(animationId);
  }
});

2. 手动调整时间差 (适用于需要精确计时的场景)

使用 setTimeoutsetInterval 时,需要手动记录时间差并进行补偿。

let lastTime = performance.now();
let timerId;

function myTimer() {
  const currentTime = performance.now();
  const deltaTime = currentTime - lastTime; // 计算时间差

  // 执行你的定时器逻辑,并根据 deltaTime 调整
  // ...

  lastTime = currentTime; // 更新 lastTime

  timerId = setTimeout(myTimer, 1000 - deltaTime % 1000); // 调整下次执行的时间
}

// 启动定时器
timerId = setTimeout(myTimer, 1000);

// 可选:在标签页失去焦点时暂停定时器,重新获得焦点时继续
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    lastTime = performance.now(); // 更新 lastTime
    timerId = setTimeout(myTimer, 1000);
  } else {
    clearTimeout(timerId);
  }
});

选择哪种方法?

  • 对于动画、平滑过渡等视觉效果,强烈推荐使用 requestAnimationFrame
  • 对于需要精确计时的场景(例如倒计时、计时器),可以使用手动调整时间差的方法。但需要注意,即使这样也无法保证绝对的精度,因为浏览器可能会对后台标签页的定时器进行更 aggressive 的节流。

总结

以上两种方法都能有效解决标签页切换导致定时器停止的问题。选择哪种方法取决于你的具体需求。记住要处理 visibilitychange 事件,以便在标签页重新获得焦点时恢复定时器。

希望这些信息能帮到你!

posted @ 2024-12-03 09:04  王铁柱6  阅读(462)  评论(0)    收藏  举报