setInterval() 的替代方法 - 在计算机 locked/doing 其他 Chrome 中保持时间

Alternative to setInterval() - Keep time while computer is locked/doing other stuff in Chrome

我有一个计时器来跟踪用户花在做某事上的时间。有一个按钮可以将计时器重置为零。效果不错。

但是,当用户离开时(即休息 15 或 30 分钟),必须锁定计算机。我注意到 return 上的时间不一致。有时,直到用户 return 秒,计时器才会开始计时。有时是几秒钟,或几分钟,但从来都不是用户的时间 - 总是 LESS。

例如,如果我现在锁定 PC,计时器开启 5 分 38 秒,然后我休息 15 分钟,我通常会 return是我离开的时候,也可能是6分5秒。

我该如何解决这个问题?有什么方法可以确保 setInterval() 继续保持一致的行为,还是我最好做些其他事情(例如比较时间戳而不是像我现在这样的独立计数器)?

就其价值而言,现在我们使用的是 VDI 工作站,这似乎更加不一致,尽管我不知道这是否会有所不同(该问题也发生在普通工作站上)。

编辑:我在另一个问题中注意到这似乎也是 Chrome 的问题(也许)?以及当 window 或选项卡不处于活动状态时它如何节流。 --app-mode windows 是否有解决方法?

如果您将开始时间设置为变量,那么您可以通过从当前时间减去您存储为变量的时间戳来计算间隔中计数器的当前时间。所以,即使计算机锁定时间隔暂停,当您解锁它并且间隔恢复时,它也会计算出正确的时间。

您也可以使用 window.sessionStorage 来存储开始时间。我有一个在 CodePen 中创建的计时器,它使用会话存储来存储值。你可以在这里看到:https://codepen.io/steebn/pen/eYWrPeM

在下面的代码片段中,我修改了该代码笔中的代码以使用全局变量而不是会话存储,因为显然您不能在代码片段中使用会话存储。

希望这对您有所帮助。

const qs = (selector) => document.querySelector(selector);
const qsa = (selector) => document.querySelectorAll(selector);

//variable to store the timestamps 
const tLog = {
  startTime: null,
  stopTime: null,
  duration: null
};

//variable to store the interval 
let timerInterval; 

//Add event listeners to each button
qsa(".timer").forEach((btn) => {
  btn.addEventListener("click", (event) => {
    qsa(`button.timer`).forEach((btn) => (btn.disabled = true));
    qs("#time").classList.remove(`paused`);
    switch (event.target.id) {
      case `start`:
        setMessage(`Timer Counting`);
        qsa(`button.started`).forEach((btn) => (btn.disabled = false));        
        tLog.startTime = tLog.duration ?
          new Date().getTime() - tLog.duration :
          new Date().getTime();
          
        //Start the timer interval  
        timerInterval = window.setInterval(() => {
          const currentTime = new Date().getTime();
          //Calculate duration using the starting timestamp in the tLog variable 
          qs("#time").innerText = getDuration(currentTime - tLog.startTime);
        }, 1000);
        
        break;
      case `stop`:
        setMessage(`Timer Complete`);
        qsa(`button.stopped`).forEach((btn) => (btn.disabled = false));
        window.clearInterval(timerInterval); //Clear the interval
        tLog.stopTime = new Date().getTime();
        tLog.duration = tLog.stopTime - tLog.startTime;
        break;
      case `pause`:
        qs(`#time`).classList.add(`paused`);
        setMessage(`Timer Paused`);
        qsa(`button.paused`).forEach((btn) => (btn.disabled = false));
        window.clearInterval(timerInterval);
        tLog.stopTime = new Date().getTime();
        tLog.duration = tLog.stopTime - tLog.startTime;
        break;
      case `reset`:
        qsa(`button.cleared`).forEach((btn) => (btn.disabled = false));
        qs("#time").innerText = `00:00:00`;
        qs("#time").classList.remove(`paused`);
        Object.keys(tLog).map((key) => (tLog.key = null));
        setMessage(`Timer is ready to begin`);
        break;
    }
  });
});

const setMessage = (msg) => (qs(".status").innerText = msg);

const getDuration = (timeStamp) => {
  const d = new Date(timeStamp);
  const H = d.getUTCHours().toString().padStart(2, 0);
  const M = d.getUTCMinutes().toString().padStart(2, 0);
  const S = d.getUTCSeconds().toString().padStart(2, 0);
  const s = d.getUTCSeconds().toString().padStart(3, 0);
  return `${H}:${M}:${S}`;
};
.btn {
  font-size: 3rem !important;
}

#time {
  position: relative;
  font-size: 4rem;
}

#time.paused {
  animation: animate 1.25s linear infinite;
}

@keyframes animate {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css' integrity='sha512-usVBAd66/NpVNfBge19gws2j6JZinnca12rAe2l+d+QkLU9fiG02O1X8Q6hepIpr/EYKZvKx/I9WsnujJuOmBA==' crossorigin='anonymous' />

<body class="bg-dark text-white">
  <header class="container d-flex my-0">
    <div class="btn-group btn-group-lg mx-auto" role="group" aria-label="Basic example">
      <button id="start" class="btn p-0 timer cleared paused">&#9654;&#65039;</button>
      <button id="pause" class="btn p-0 timer started" disabled>&#9208;&#65039;</button>
      <button id="stop" class="btn p-0 timer started paused" disabled>&#9209;&#65039;</button>
      <button id="reset" class="btn p-0 timer stopped" disabled>&#128259;</button>
    </div>
  </header>
  <main class="container d-flex my-0 flex-column">
    <div id="time" class="mx-auto">00:00:00</div>
    <div class="mx-auto status">Timer is ready to begin</div>
  </main>
</body>