计时器在重复点击开始/重置按钮时的时间问题。

huangapple go评论83阅读模式
英文:

Stopwatch time issue on repeated start/reset buttons click

问题

当我点击开始按钮时,计数开始,开始按钮隐藏并显示暂停和重置按钮。如果我反复点击开始和重置按钮,计数将不再正确计数,重置按钮和暂停按钮也将不再起作用。

var c = 0;
var t;

function count() {
  document.getElementById("count").innerHTML = c;
  c = c + 1;
  t = setTimeout(count, 1000);
}

function start() {
  count();
  document.getElementById("startb").style.display = "none";
  document.getElementById("pauseb").style.display = "inline";
}

function pause() {
  clearTimeout(t);
  document.getElementById("startb").style.display = "inline";
  document.getElementById("pauseb").style.display = "none";
  document.getElementById("startb").innerHTML = "继续";
  document.getElementById("resetb").style.display = "inline";
}

function reset() {
  c = 0;
  clearTimeout(t);
  document.getElementById("count").innerHTML = c;
  document.getElementById("startb").style.display = "inline";
  document.getElementById("startb").innerHTML = "开始";
  document.getElementById("pauseb").style.display = "none";
  document.getElementById("resetb").style.display = "none";
}
* {
  margin: 0;
  padding: 0;
  font-family: Arial;
}

body {
  background-color: #333;
  color: black;
}

button {
  width: 100px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  color: #000;
  background-color: #c3c3c3;
  border: 1px solid #000;
}

#count {
  font-size: 175px;
}

#startb {
  background-color: #800080;
  border-color: #220022;
  color: #2e002e;
}

#pauseb {
  display: none;
  background-color: black;
  color: #b6b6b6;
}

#resetb {
  display: none;
  background-color: red;
  color: #3b0000;
  border-color: #3b0000;
  font-weight: bold;
}

#lapb {
  display: none;
  background-color: #008800;
  color: black;
  border-color: darkgreen;
  font-weight: bold;
}
<h1>秒表</h1>
<span id="count">0</span><br />
<button id="startb" onClick="start()">开始</button>
<button id="pauseb" onClick="pause()">暂停</button>
<button id="resetb" onClick="reset()">重置</button>
<button id="lapb" onClick="lap()">圈数</button>
英文:

When I click start, the count begins, and the start button hides and shows the pause and reset buttons. If I click start and reset repeatedly, the count will no longer count correctly and the reset buttons and pause buttons won't even matter anymore.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var c = 0;
var t;

function count() {
  document.getElementById(&quot;count&quot;).innerHTML = c;
  c = c + 1;
  t = setTimeout(count, 1000);
}

function start() {
  count();
  document.getElementById(&quot;startb&quot;).style.display = &quot;none&quot;;
  document.getElementById(&quot;pauseb&quot;).style.display = &quot;inline&quot;;
}

function pause() {
  clearTimeout(t);
  document.getElementById(&quot;startb&quot;).style.display = &quot;inline&quot;;
  document.getElementById(&quot;pauseb&quot;).style.display = &quot;none&quot;;
  document.getElementById(&quot;startb&quot;).innerHTML = &quot;Resume&quot;;
  document.getElementById(&quot;resetb&quot;).style.display = &quot;inline&quot;;
}

function reset() {
  c = 0;
  clearTimeout(t);
  document.getElementById(&quot;count&quot;).innerHTML = c;
  document.getElementById(&quot;startb&quot;).style.display = &quot;inline&quot;;
  document.getElementById(&quot;startb&quot;).innerHTML = &quot;Start&quot;;
  document.getElementById(&quot;pauseb&quot;).style.display = &quot;none&quot;;
  document.getElementById(&quot;resetb&quot;).style.display = &quot;none&quot;;
}

<!-- language: lang-css -->

* {
  margin: 0;
  padding: 0;
  font-family: Arial;
}

body {
  background-color: #333;
  color: black;
}

button {
  width: 100px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  color: #000;
  background-color: #c3c3c3;
  border: 1px solid #000;
}

#count {
  font-size: 175px;
}

#startb {
  background-color: #800080;
  border-color: #220022;
  color: #2e002e;
}

#pauseb {
  display: none;
  background-color: black;
  color: #b6b6b6;
}

#resetb {
  display: none;
  background-color: red;
  color: #3b0000;
  border-color: #3b0000;
  font-weight: bold;
}

#lapb {
  display: none;
  background-color: #008800;
  color: black;
  border-color: darkgreen;
  font-weight: bold;
}

<!-- language: lang-html -->

&lt;h1&gt;Stopwatch&lt;/h1&gt;
&lt;span id=&quot;count&quot;&gt;0&lt;/span&gt;&lt;br /&gt;
&lt;button id=&quot;startb&quot; onClick=&quot;start()&quot;&gt;Start&lt;/button&gt;
&lt;button id=&quot;pauseb&quot; onClick=&quot;pause()&quot;&gt;Pause&lt;/button&gt;
&lt;button id=&quot;resetb&quot; onClick=&quot;reset()&quot;&gt;Reset&lt;/button&gt;
&lt;button id=&quot;lapb&quot; onClick=&quot;lap()&quot;&gt;Lap&lt;/button&gt;

<!-- end snippet -->

i havent tried anything im guessing multiple instances of the start function are being processed, im thinking if else statements somewhere could prevent this.

答案1

得分: 1

  • 不要使用 setInterval(尤其不要将间隔设置得那么频繁)来频繁更新您的用户界面。您不希望使用 setInterval 阻塞主线程,而是使用性能最佳的方法,即 Window.requestAnimationFrame

  • 在HTML中,不要使用ID。为什么不在单个页面上拥有多个监听器?如果使用ID,您的代码将紧密耦合到一个元素上。改为使用类。

  • 避免从JS中使用 Element.style,而是使用Element.classList方法,如 .add().remove()

以下是一个更好且性能更高的示例,显示了一个格式为 h+:m:s.mil 的秒表:

<div class="stopwatch">
  <span class="stopwatch-count"></span><br />
  <button class="stopwatch-start">Start</button>
  <button class="stopwatch-resume">Resume</button>
  <button class="stopwatch-pause">Pause</button>
  <button class="stopwatch-reset">Reset</button>
</div>
* {
  box-sizing: border-box;
  margin: 0;
  font-family: Arial;
}

button {
  border: 1px solid #000;
  color: #fff;
  min-width: 5rem;
  padding: 0.6em;
}

.stopwatch-count {
  font-size: 3rem;
}

.stopwatch-start {
  background-color: green;
}

.stopwatch-resume {
  display: none;
  background-color: blue;
}

.stopwatch-pause {
  display: none;
  background-color: black;
}

.stopwatch-reset {
  display: none;
  background-color: red;
}

.isStarted .stopwatch-start {
  display: none;
}

.isStarted .stopwatch-pause {
  display: initial;
}

.isPaused .stopwatch-pause {
  display: none;
}

.isPaused .stopwatch-reset {
  display: initial;
}

.isPaused .stopwatch-resume {
  display: initial;
}
const pad = (int, pad) => String(int).padStart(pad, "0");

const stopwatch = (el) => {
  const elCount = el.querySelector(".stopwatch-count");
  const elStart = el.querySelector(".stopwatch-start");
  const elResume = el.querySelector(".stopwatch-resume");
  const elPause = el.querySelector(".stopwatch-pause");
  const elReset = el.querySelector(".stopwatch-reset");

  let timeStart;
  let timeElapsed;
  let raf;

  const draw = () => {
    const mil = timeElapsed % 1000;
    const sec = Math.floor(timeElapsed / 1000 % 60);
    const min = Math.floor(timeElapsed / 1000 / 60 % 60);
    const hou = Math.floor(timeElapsed / 1000 / 60 / 60);
    elCount.textContent = `${pad(hou)}:${pad(min, 2)}:${pad(sec, 2)}.${pad(mil, 3)}`;
  };

  const count = () => {
    timeElapsed = Date.now() - timeStart;
    draw();
    raf = requestAnimationFrame(count);
  };

  const start = () => {
    timeStart = Date.now() - timeElapsed;
    el.classList.remove("isPaused");
    el.classList.add("isStarted");
    count();
  };

  const pause = () => {
    cancelAnimationFrame(raf);
    el.classList.add("isPaused");
  };

  const reset = () => {
    cancelAnimationFrame(raf);
    timeStart = 0;
    timeElapsed = 0;
    timeDiff = 0;
    el.classList.remove("isPaused");
    el.classList.remove("isStarted");
    draw();
  };

  // 事件:
  elStart.addEventListener("click", start);
  elPause.addEventListener("click", pause);
  elResume.addEventListener("click", start);
  elReset.addEventListener("click", reset);

  // 初始化:
  reset();
};

// 初始化:
document.querySelectorAll(".stopwatch").forEach(stopwatch);
英文:
  • Don't use setInterval (specially not set to such an expensive ms interval) when in need to do frequent updates to your UI. You don't want to cog the main thread with setInterval, instead use the absolute winner in performance, and that's the Window.requestAnimationFrame.
  • In HTML, don't use IDs. Why not have several watches on a single page? If you use ID-s you code becomes tightly coupled to one element only. Use classes instead
  • Avoid the use of Element.style from JS, instead use the Element.classList methods like .add(), .remove()

Here's a better and performant example for a stopwatch that previews h+:m:s.mil

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const pad = (int, pad) =&gt; String(int).padStart(pad, &quot;0&quot;);
const stopwatch = (el) =&gt; {
const elCount = el.querySelector(&quot;.stopwatch-count&quot;);
const elStart = el.querySelector(&quot;.stopwatch-start&quot;);
const elResume = el.querySelector(&quot;.stopwatch-resume&quot;);
const elPause = el.querySelector(&quot;.stopwatch-pause&quot;);
const elReset = el.querySelector(&quot;.stopwatch-reset&quot;);
let timeStart;
let timeElapsed;
let raf;
const draw = () =&gt; {
const mil = timeElapsed % 1000;
const sec = Math.floor(timeElapsed / 1000 % 60);
const min = Math.floor(timeElapsed / 1000 / 60 % 60);
const hou = Math.floor(timeElapsed / 1000 / 60 / 60);
elCount.textContent = `${pad(hou)}:${pad(min, 2)}:${pad(sec, 2)}.${pad(mil, 3)}`;
};
const count = () =&gt; {
timeElapsed = Date.now() - timeStart;
draw();
raf = requestAnimationFrame(count);
};
const start = () =&gt; {
timeStart = Date.now() - timeElapsed;
el.classList.remove(&quot;isPaused&quot;);
el.classList.add(&quot;isStarted&quot;);
count();
};
const pause = () =&gt; {
cancelAnimationFrame(raf);
el.classList.add(&quot;isPaused&quot;);
};
const reset = () =&gt; {
cancelAnimationFrame(raf);
timeStart = 0;
timeElapsed = 0;
timeDiff = 0;
el.classList.remove(&quot;isPaused&quot;);
el.classList.remove(&quot;isStarted&quot;);
draw();
};
// Events:
elStart.addEventListener(&quot;click&quot;, start);
elPause.addEventListener(&quot;click&quot;, pause);
elResume.addEventListener(&quot;click&quot;, start);
elReset.addEventListener(&quot;click&quot;, reset);
// Init:
reset();
};
// Initialize:
document.querySelectorAll(&quot;.stopwatch&quot;).forEach(stopwatch);

<!-- language: lang-css -->

* {
box-sizing: border-box;
margin: 0;
font-family: Arial;
}
button {
border: 1px solid #000;
color: #fff;
min-width: 5rem;
padding: 0.6em;
}
.stopwatch-count {
font-size: 3rem;
}
.stopwatch-start {
background-color: green;
}
.stopwatch-resume {
display: none;
background-color: blue;
}
.stopwatch-pause {
display: none;
background-color: black;
}
.stopwatch-reset {
display: none;
background-color: red;
}
.isStarted .stopwatch-start {
display: none;
}
.isStarted .stopwatch-pause {
display: initial;
}
.isPaused .stopwatch-pause {
display: none;
}
.isPaused .stopwatch-reset {
display: initial;
}
.isPaused .stopwatch-resume {
display: initial;
}

<!-- language: lang-html -->

&lt;div class=&quot;stopwatch&quot;&gt;
&lt;span class=&quot;stopwatch-count&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;button class=&quot;stopwatch-start&quot;&gt;Start&lt;/button&gt;
&lt;button class=&quot;stopwatch-resume&quot;&gt;Resume&lt;/button&gt;
&lt;button class=&quot;stopwatch-pause&quot;&gt;Pause&lt;/button&gt;
&lt;button class=&quot;stopwatch-reset&quot;&gt;Reset&lt;/button&gt;
&lt;/div&gt;

<!-- end snippet -->

and will work for any number of stopwatch on a single page.

答案2

得分: -1

尝试这个。它的精确度可达毫秒。

var startTime;
var elapsedTime = 0;
var timerInterval;

function start() {
  startTime = Date.now() - elapsedTime;
  if (!timerInterval) {
    timerInterval = setInterval(updateTime, 10);
  }
}

function pause() {
  clearInterval(timerInterval);
  timerInterval = null;
}

function reset() {
  clearInterval(timerInterval);
  timerInterval = null;
  elapsedTime = 0;
  var formattedTime = formatTime(elapsedTime);
  document.querySelector(".time").textContent = formattedTime;
}

function updateTime() {
  var currentTime = Date.now();
  elapsedTime = currentTime - startTime;
  var formattedTime = formatTime(elapsedTime);
  document.querySelector(".time").textContent = formattedTime;
}

function formatTime(time) {
  var hours = Math.floor(time / 3600000);
  var minutes = Math.floor((time % 3600000) / 60000);
  var seconds = Math.floor((time % 60000) / 1000);
  var milliseconds = Math.floor((time % 1000) / 10);

  return (
    pad(hours, 2) +
    ":" +
    pad(minutes, 2) +
    ":" +
    pad(seconds, 2) +
    "." +
    pad(milliseconds, 2)
  );
}

function pad(value, count) {
  var padding = "";
  for (var i = 0; i < count; i++) {
    padding += "0";
  }
  return (padding + value).slice(-count);
}
.container {
  text-align: center;
  margin-top: 100px;
}

.time {
  font-size: 48px;
  margin-bottom: 20px;
}

.buttons {
  margin-top: 20px;
}
<!DOCTYPE html>
<html>

<head>
  <title>秒表</title>
</head>

<body>
  <div class="container">
    <div class="time">00:00:00</div>
    <div class="buttons">
      <button onclick="start()">开始/继续</button>
      <button onclick="pause()">暂停</button>
      <button onclick="reset()">重置</button>
    </div>
  </div>
</body>

</html>
英文:

Try this. It has precision upto milliseconds

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

var startTime;
var elapsedTime = 0;
var timerInterval;
function start() {
startTime = Date.now() - elapsedTime;
if (!timerInterval) {
timerInterval = setInterval(updateTime, 10);
}
}
function pause() {
clearInterval(timerInterval);
timerInterval = null;
}
function reset() {
clearInterval(timerInterval);
timerInterval = null;
elapsedTime = 0;
var formattedTime = formatTime(elapsedTime);
document.querySelector(&quot;.time&quot;).textContent = formattedTime;
}
function updateTime() {
var currentTime = Date.now();
elapsedTime = currentTime - startTime;
var formattedTime = formatTime(elapsedTime);
document.querySelector(&quot;.time&quot;).textContent = formattedTime;
}
function formatTime(time) {
var hours = Math.floor(time / 3600000);
var minutes = Math.floor((time % 3600000) / 60000);
var seconds = Math.floor((time % 60000) / 1000);
var milliseconds = Math.floor((time % 1000) / 10);
return (
pad(hours, 2) +
&quot;:&quot; +
pad(minutes, 2) +
&quot;:&quot; +
pad(seconds, 2) +
&quot;.&quot; +
pad(milliseconds, 2)
);
}
function pad(value, count) {
var padding = &quot;&quot;;
for (var i = 0; i &lt; count; i++) {
padding += &quot;0&quot;;
}
return (padding + value).slice(-count);
}

<!-- language: lang-css -->

.container {
text-align: center;
margin-top: 100px;
}
.time {
font-size: 48px;
margin-bottom: 20px;
}
.buttons {
margin-top: 20px;
}

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Stopwatch&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;div class=&quot;time&quot;&gt;00:00:00&lt;/div&gt;
&lt;div class=&quot;buttons&quot;&gt;
&lt;button onclick=&quot;start()&quot;&gt;Start/Resume&lt;/button&gt;
&lt;button onclick=&quot;pause()&quot;&gt;Pause&lt;/button&gt;
&lt;button onclick=&quot;reset()&quot;&gt;Reset&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->

Edit:

Request animation frame is not the right solution for this use case for the following reasons:

  1. There is no animation going on in here.
  2. Even if there was animation going on requestAnimationFrame calls are paused when running in background tabs or hidden iframe tags which is not ideal for a stopwatch app

Read more about requestAnimationFrame on MDN

huangapple
  • 本文由 发表于 2023年6月29日 01:09:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76575356.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定