如何使用JavaScript创建一个秒表?

43
if(stopwatch >= track[song].duration)

track[song].duration 查找 soundcloud 音轨的持续时间。

我想创建一个秒表函数,当您单击 swap ID stopwatch 时开始计算毫秒,以便在“点击”函数达到一定时间时 if 函数会执行某些操作。在我的情况下,替换图像。还希望在再次单击时该函数将自动重置。

因此,类似于 stopwatch = current time - clicked time 如何设置 clicked time

current time = new Date().getTime();?这是以毫秒为单位吗?

$('#swap').click(function()...

JavaScript中的计时器通常是通过在特定时刻创建日期对象来设置的。然后,您可以减去日期以获取毫秒级别的差异。但似乎您已经知道了这一点。缺少的是您想要为其创建日期对象的事件。 - RobG
7个回答

99

你会看到演示代码只是一个 开始/停止/重置 毫秒计数器。如果你想在时间上进行花式格式化,完全取决于你。这应该足以让你开始。

这是一个有趣的小项目。这是我的方法:

var Stopwatch = function(elem, options) {

  var timer = createTimer(),
    startButton = createButton("start", start),
    stopButton = createButton("stop", stop),
    resetButton = createButton("reset", reset),
    offset,
    clock,
    interval;

  // default options
  options = options || {};
  options.delay = options.delay || 1;

  // append elements     
  elem.appendChild(timer);
  elem.appendChild(startButton);
  elem.appendChild(stopButton);
  elem.appendChild(resetButton);

  // initialize
  reset();

  // private functions
  function createTimer() {
    return document.createElement("span");
  }

  function createButton(action, handler) {
    var a = document.createElement("a");
    a.href = "#" + action;
    a.innerHTML = action;
    a.addEventListener("click", function(event) {
      handler();
      event.preventDefault();
    });
    return a;
  }

  function start() {
    if (!interval) {
      offset = Date.now();
      interval = setInterval(update, options.delay);
    }
  }

  function stop() {
    if (interval) {
      clearInterval(interval);
      interval = null;
    }
  }

  function reset() {
    clock = 0;
    render(0);
  }

  function update() {
    clock += delta();
    render();
  }

  function render() {
    timer.innerHTML = clock / 1000;
  }

  function delta() {
    var now = Date.now(),
      d = now - offset;

    offset = now;
    return d;
  }

  // public API
  this.start = start;
  this.stop = stop;
  this.reset = reset;
};


// basic examples
var elems = document.getElementsByClassName("basic");

for (var i = 0, len = elems.length; i < len; i++) {
  new Stopwatch(elems[i]);
}


// programmatic examples
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();

var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {
  delay: 100
});
bTimer.start();

var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {
  delay: 456
});
cTimer.start();

var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {
  delay: 1000
});
dTimer.start();
.stopwatch {
  display: inline-block;
  background-color: white;
  border: 1px solid #eee;
  padding: 5px;
  margin: 5px;
}

.stopwatch span {
  font-weight: bold;
  display: block;
}

.stopwatch a {
  padding-right: 5px;
  text-decoration: none;
}
<h2>Basic example; update every 1 ms</h2>

<p>click <code>start</code> to start a stopwatch</p>

<pre>
var elems = document.getElementsByClassName("basic");
  
for (var i=0, len=elems.length; i&lt;len; i++) {
  new Stopwatch(elems[i]);
}
</pre>
<div class="basic stopwatch"></div>
<div class="basic stopwatch"></div>

<hr>
<h2>Programmatic example</h2>

<p><strong>Note:</strong> despite the varying <code>delay</code> settings, each stopwatch displays the correct time (in seconds)</p>

<pre>
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();
</pre>
<div class="stopwatch" id="a-timer"></div>1 ms<br>

<pre>
var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {delay: 100});
bTimer.start();
</pre>
<div class="stopwatch" id="b-timer"></div>100 ms<br>

<pre>
var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {delay: 456});
cTimer.start();
</pre>
<div class="stopwatch" id="c-timer"></div>456 ms<br>

<pre>
var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {delay: 1000});
dTimer.start();
</pre>
<div class="stopwatch" id="d-timer"></div>1000 ms<br>

获取一些基本的HTML包装器

<!-- create 3 stopwatches -->
<div class="stopwatch"></div>
<div class="stopwatch"></div>
<div class="stopwatch"></div>

从那里开始使用非常简单。

var elems = document.getElementsByClassName("stopwatch");

for (var i=0, len=elems.length; i<len; i++) {
    new Stopwatch(elems[i]);
}

作为额外福利,你还可以获得一个可编程的计时器API。以下是用法示例

var elem = document.getElementById("my-stopwatch");
var timer = new Stopwatch(elem, {delay: 10});

// start the timer
timer.start();

// stop the timer
timer.stop();

// reset the timer
timer.reset();

jQuery 插件

至于 jQuery 部分,一旦您拥有像上面那样的优美代码组合,编写一个 jQuery 插件 就非常简单了。

(function($) {
    var Stopwatch = function(elem, options) {
    // code from above...
    };

    $.fn.stopwatch = function(options) {
    return this.each(function(idx, elem) {
        new Stopwatch(elem, options);
    });
    };
})(jQuery);

jQuery插件使用:

// all elements with class .stopwatch; default delay (1 ms)
$(".stopwatch").stopwatch();

// a specific element with id #my-stopwatch; custom delay (10 ms)
$("#my-stopwatch").stopwatch({delay: 10});

jsbin.com演示


在我的特定情况下,我调用timer.start(),它工作得很好,但是当我必须使用timer.stop()时,'timer'变量超出了范围,我该怎么办? - user2134555
感谢您的关注。不过,我几个小时前就已经解决了这个问题。 - user2134555
这是一段非常好的代码!但是,有一个问题需要注意。如果在计时器运行时系统时间发生变化,计时器的值将会跳变。您该如何解决这个问题? - Tigran
6
如果用户重新启动计算机或刷新浏览器选项卡,计时器也会停止工作。这些并不是“陷阱”。除非有某种合理的额外要求,否则我不会编写代码来解决这些“问题”。 - maček
1
我很想看到这个使用本地类等转换为ES6。 @maček有兴趣吗? - Justin
显示剩余3条评论

41

两种原生解决方案

  • performance.now --> 调用 ... 耗时 6.414999981643632 毫秒。
  • console.time --> 调用 ... 耗时 5.815 毫秒

它们之间的区别在于精度。

详细使用和说明请继续阅读。

Performance.now (For microsecond precision use)

    var t0 = performance.now();
    doSomething();
    var t1 = performance.now();

    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
            
    function doSomething(){    
       for(i=0;i<1000000;i++){var x = i*i;}
    }

performance.now

与 JavaScript 可获取的其他时间数据(例如 Date.now())不同,Performance.now() 返回的时间戳不仅限于每毫秒一次的分辨率。相反,它们作为浮点数表示时间,具有高达微秒级别的精度。

与Date.now() 不同,Performance.now() 返回值始终以恒定速率增加,独立于系统时钟(可能会因软件如 NTP 手动调整或偏移)。否则,performance.timing.navigationStart + performance.now() 的值将近似于 Date.now()。



console.time

Example: (timeEnd wrapped in setTimeout for simulation)

console.time('Search page');
  doSomething();
console.timeEnd('Search page');
 

 function doSomething(){    
       for(i=0;i<1000000;i++){var x = i*i;}
 }

You can change the Timer-Name for different operations.


值得一提的是,performance.now 在 IE 9 上会出现问题(如果你的应用程序没有经过转译且兼容IE 9仍然很重要)。 - ruffin

14

一个简单易用的时钟,不要忘记我 ;)

var x;
var startstop = 0;

function startStop() { /* Toggle StartStop */

  startstop = startstop + 1;

  if (startstop === 1) {
    start();
    document.getElementById("start").innerHTML = "Stop";
  } else if (startstop === 2) {
    document.getElementById("start").innerHTML = "Start";
    startstop = 0;
    stop();
  }

}


function start() {
  x = setInterval(timer, 10);
} /* Start */

function stop() {
  clearInterval(x);
} /* Stop */

var milisec = 0;
var sec = 0; /* holds incrementing value */
var min = 0;
var hour = 0;

/* Contains and outputs returned value of  function checkTime */

var miliSecOut = 0;
var secOut = 0;
var minOut = 0;
var hourOut = 0;

/* Output variable End */


function timer() {
  /* Main Timer */


  miliSecOut = checkTime(milisec);
  secOut = checkTime(sec);
  minOut = checkTime(min);
  hourOut = checkTime(hour);

  milisec = ++milisec;

  if (milisec === 100) {
    milisec = 0;
    sec = ++sec;
  }

  if (sec == 60) {
    min = ++min;
    sec = 0;
  }

  if (min == 60) {
    min = 0;
    hour = ++hour;

  }


  document.getElementById("milisec").innerHTML = miliSecOut;
  document.getElementById("sec").innerHTML = secOut;
  document.getElementById("min").innerHTML = minOut;
  document.getElementById("hour").innerHTML = hourOut;

}


/* Adds 0 when value is <10 */


function checkTime(i) {
  if (i < 10) {
    i = "0" + i;
  }
  return i;
}

function reset() {


  /*Reset*/

  milisec = 0;
  sec = 0;
  min = 0
  hour = 0;

  document.getElementById("milisec").innerHTML = "00";
  document.getElementById("sec").innerHTML = "00";
  document.getElementById("min").innerHTML = "00";
  document.getElementById("hour").innerHTML = "00";

}
<h1>
  <span id="hour">00</span> :
  <span id="min">00</span> :
  <span id="sec">00</span> :
  <span id="milisec">00</span>
</h1>

<button onclick="startStop()" id="start">Start</button>
<button onclick="reset()">Reset</button>


为什么百分之一秒被称为毫秒?如果标签失去焦点,这个称呼就完全不准确了。 - gre_gor

4
这是我对这个问题的简单看法,希望能在某一天、某个地方帮助到某个人...

let output = document.getElementById('stopwatch');
let ms = 0;
let sec = 0;
let min = 0;

function timer() {
    ms++;
    if(ms >= 100){
        sec++
        ms = 0
    }
    if(sec === 60){
        min++
        sec = 0
    }
    if(min === 60){
        ms, sec, min = 0;
    }

    //Doing some string interpolation
    let milli = ms < 10 ? `0`+ ms : ms;
    let seconds = sec < 10 ? `0`+ sec : sec;
    let minute = min < 10 ? `0` + min : min;

    let timer= `${minute}:${seconds}:${milli}`;
    output.innerHTML =timer;
};
//Start timer
function start(){
 time = setInterval(timer,10);
}
//stop timer
function stop(){
    clearInterval(time)
}
//reset timer
function reset(){
    ms = 0;
    sec = 0;
    min = 0;

    output.innerHTML = `00:00:00`
}
const startBtn = document.getElementById('startBtn');
const stopBtn =  document.getElementById('stopBtn');
const resetBtn = document.getElementById('resetBtn');

startBtn.addEventListener('click',start,false);
stopBtn.addEventListener('click',stop,false);
resetBtn.addEventListener('click',reset,false);
    <p class="stopwatch" id="stopwatch">
        <!-- stopwatch goes here -->
    </p>
    <button class="btn-start" id="startBtn">Start</button>
    <button class="btn-stop" id="stopBtn">Stop</button>
    <button class="btn-reset" id="resetBtn">Reset</button>


1
有趣的是,计数器运行比“真实”时间慢。如果你比较秒数,你会发现它更慢。也许是因为if语句需要一些时间来检查,然后变慢了(即使只有几毫秒,也会累加)? - Hollul
如果选项卡失去焦点,这将变得完全不准确。 - gre_gor

2

Mosh Hamedani的解决方案

创建一个 StopWatch 函数构造器。

定义4个本地变量

  1. startTime
  2. endTime
  3. isRunning
  4. duration 设置为 0

接下来创建3个方法

  1. start
  2. stop
  3. reset

start 方法

  • 检查 isRunning 是否为 true,如果是,则抛出错误,表示不能调用两次 start。
  • 将 isRunning 设置为 true
  • 将当前日期对象分配给 startTime。

stop 方法

  • 检查 isRunning 是否为 false,如果是,则抛出错误,表示不能调用两次 stop。
  • 将 isRunning 设置为 false
  • 将当前日期对象分配给 endTime。
  • 通过 endTime 和 startTime 日期对象计算秒数
  • 使用秒数增加 duration

reset 方法:

  • 重置所有本地变量。

只读属性

如果要访问 duration 本地变量,需要使用 Object.defineProperty 定义一个属性。 当你想创建一个 只读 属性时,这非常有用。

Object.defineProperty 接受 3 个参数

  • 要定义属性的对象(在本例中为当前对象(this))
  • 属性的名称
  • 键属性的值。

  • 我们想创建一个只读属性,因此将一个 对象 作为 传递。 该对象包含一个 get 方法,返回 duration 本地变量。 通过这种方式,我们无法更改属性,只能 获取 它。

技巧是使用 Date() 对象计算时间。

参考下面的代码

function StopWatch() {


let startTime,
    endTime,
    isRunning,
    duration = 0;

  this.start = function () {
    if (isRunning) throw new Error("StopWatch has already been started.");

    isRunning = true;

    startTime = new Date();
  };



this.stop = function () {
    if (!isRunning) throw new Error("StopWatch has already been stop.");

    isRunning = false;

    endTime = new Date();

    const seconds = (endTime.getTime() - startTime.getTime()) / 1000;
    duration += seconds;
  };

  this.reset = function () {
    duration = 0;
    startTime = null;
    endTime = null;
    isRunning = false;
  };

  Object.defineProperty(this, "duration", {
    get: function () {
      return duration;
    },
  });
}

const sw = new StopWatch();

我一直得到的 sw.duration 值为 0。 - kunal verma

1
function StopWatch() {
    let startTime, endTime, running, duration = 0
    
    this.start = () => {
        if (running) console.log('its already running')
        else {
            running = true
            startTime = Date.now()
        }
    }

    this.stop = () => {
        if (!running) console.log('its not running!')
        else {
            running = false
            endTime = Date.now()

            const seconds = (endTime - startTime) / 1000
            duration += seconds
        }
    }

    this.restart = () => {
        startTime = endTime = null
        running = false
        duration = 0
    }
    
    Object.defineProperty(this, 'duration', {
        get: () => duration.toFixed(2)
    })

}

const sw =  new StopWatch()

sw.start()
sw.stop()

sw.duration

0

在Mace提供的代码进行了一些修改之后,我最终构建了一个秒表。 https://codepen.io/truestbyheart/pen/EGELmv

  <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Stopwatch</title>
    <style>
    #center {
     margin: 30%  30%;
     font-family: tahoma;
     }
    .stopwatch {
         border:1px solid #000;
         background-color: #eee;
         text-align: center;
         width:656px;
         height: 230px;
         overflow: hidden;
     }
     .stopwatch span{
         display: block;
         font-size: 100px;
     }
     .stopwatch p{
         display: inline-block;
         font-size: 40px;
     }
     .stopwatch a{
       font-size:45px;
     }
     a:link,
     a:visited{
         color :#000;
         text-decoration: none;
         padding: 12px 14px;
         border: 1px solid #000;
     }
    </style>
  </head>
  <body>
      <div id="center">
            <div class="timer stopwatch"></div>
      </div>

    <script>
      const Stopwatch = function(elem, options) {
        let timer = createTimer(),
          startButton = createButton("start", start),
          stopButton = createButton("stop", stop),
          resetButton = createButton("reset", reset),
          offset,
          clock,
          interval,
          hrs = 0,
          min = 0;

        // default options
        options = options || {};
        options.delay = options.delay || 1;

        // append elements
        elem.appendChild(timer);
        elem.appendChild(startButton);
        elem.appendChild(stopButton);
        elem.appendChild(resetButton);

        // initialize
        reset();

        // private functions
        function createTimer() {
          return document.createElement("span");
        }

        function createButton(action, handler) {
          if (action !== "reset") {
            let a = document.createElement("a");
            a.href = "#" + action;
            a.innerHTML = action;
            a.addEventListener("click", function(event) {
              handler();
              event.preventDefault();
            });
            return a;
          } else if (action === "reset") {
            let a = document.createElement("a");
            a.href = "#" + action;
            a.innerHTML = action;
            a.addEventListener("click", function(event) {
              clean();
              event.preventDefault();
            });
            return a;
          }
        }

        function start() {
          if (!interval) {
            offset = Date.now();
            interval = setInterval(update, options.delay);
          }
        }

        function stop() {
          if (interval) {
            clearInterval(interval);
            interval = null;
          }
        }

        function reset() {
          clock = 0;
          render(0);
        }

        function clean() {
          min = 0;
          hrs = 0;
          clock = 0;
          render(0);
        }

        function update() {
          clock += delta();
          render();
        }

        function render() {
          if (Math.floor(clock / 1000) === 60) {
            min++;
            reset();
            if (min === 60) {
              min = 0;
              hrs++;
            }
          }
          timer.innerHTML =
            hrs + "<p>hrs</p>" + min + "<p>min</p>" + Math.floor(clock / 1000)+ "<p>sec</p>";
        }

        function delta() {
          var now = Date.now(),
            d = now - offset;

          offset = now;
          return d;
        }
      };

      // Initiating the Stopwatch
      var elems = document.getElementsByClassName("timer");

      for (var i = 0, len = elems.length; i < len; i++) {
        new Stopwatch(elems[i]);
      }
    </script>
  </body>
</html>

秒数经常超过60,某些计算需要更多时间,在这种情况下,渲染方法中的第一个“if”条件永远不会得到满足。 - Dharmesh Tailor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接