L'orologio Javascript conta oltre 60 minuti e secondi

Sep 29 2020

Ho creato un orologio Javascript, quando i secondi e i minuti raggiungono 60 conterà oltre 60 e continuerà ...

function increment(){
  if(running == 1){
    setTimeout(function(){
      Dtime++;
      var hours = Math.floor(Dtime / 10 / 3600);
      if(hours <= 9){
        hours = "0" + hours;
      }
      var mins = Math.floor(Dtime / 10 / 60);
      if(mins <= 9){
        mins = "0" + mins;
      }
      var secs = Math.floor(Dtime / 10);
      if(secs <= 9){
        secs = "0" + secs;
      }
      document.getElementById("outputt").innerHTML = hours + ":" + mins + ":" + secs;
      increment();
    }, 100);
  }
}

var Dtime=0;
var running = 1;
increment();
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>

Fondamentalmente l'orologio conta oltre 60 minuti e secondi, vorrei che questo si resettasse a 0 invece di colpire 60. Non ho avuto il tempo di testare le ore anche se sono sicuro che andrebbe anche oltre le 24.

Qualcuno può spiegare come sarebbe stato fatto? Sono certo che sia molto semplice, ma sono riuscito a lottare con esso per un po 'troppo a lungo e mi sto frustrando.

Risposte

2 Ivar Sep 29 2020 at 20:46

Puoi utilizzare l' operatore resto ( %) per tornare a 0 quando un numero supera 60.

Per esempio

  • 59 % 60 === 59
  • 60 % 60 === 0
  • 61 % 60 === 1

Quindi il tuo codice sarebbe simile a questo:

function increment() {
  if (running == 1) {
    setTimeout(function() {
      Dtime++;
      var hours = Math.floor(Dtime / 10 / 3600);
      if (hours <= 9) {
        hours = "0" + hours;
      }
      var mins = Math.floor(Dtime / 10 / 60) % 60; // Remainder operator
      if (mins <= 9) {
        mins = "0" + mins;
      }
      var secs = Math.floor(Dtime / 10) % 60; // Remainder operator
      if (secs <= 9) {
        secs = "0" + secs;
      }
      document.getElementById("outputt").innerHTML = hours + ":" + mins + ":" + secs;
      increment();
    }, 100);
  }
}

var running = 1;
var Dtime = 35980; //Setting it just under 1 hour for testing purposes
increment();
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>

Tieni presente che questo timer potrebbe variare nel tempo. Per sapere perché e come risolverlo, vedi Come creare un timer accurato in javascript?

2 RickardElimää Sep 29 2020 at 21:32

Non puoi fidarti che setTimeout o setInterval verranno eseguiti esattamente in X millisecondi. Guarda il seguente video sui loop di eventi per capire perché. È qualcosa che dovresti sapere quando programmi javascript.

Quello che devi fare invece è aggiornare l'elemento di output ogni volta che lo schermo viene ridisegnato, cosa che puoi fare con requestAnimationFrame . È necessario memorizzare quando l'orologio si avvia, operazione che può essere eseguita in millisecondi, quindi calcolare da lì la differenza tra l'ora di inizio e l'ora corrente (con Date.now()) quando la finestra sta per ridipingere. requestAnimationFrameaccetta un callback come parametro, che dovrebbe essere il metodo che aggiorna l'elemento di output.

EDIT: ho accelerato il timer per mostrare il minuto e il secondo reset.

const outputElement = document.getElementById("output");
var startTime = 0;

function startTimer() {
  startTime = Date.now();
  updateTimer();
}

function updateTimer() {
  let differenceInMillis = Date.now() - startTime;
  let {hours, minutes, seconds} = calculateTime(differenceInMillis);
  let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;

  outputElement.innerText = timeStr;

  requestAnimationFrame(updateTimer);
}

function calculateTime(timeInMillis) {
  const SECONDS = 10; // should be 1000 - only 10 to speed up the timer
  const MINUTES = 60;
  const HOURS   = 60;
  const RESET   = 60;
  
  let hours   = Math.floor(timeInMillis / SECONDS / MINUTES / HOURS);
  let minutes = Math.floor(timeInMillis / SECONDS / MINUTES) % RESET;
  let seconds = Math.floor(timeInMillis / SECONDS) % RESET;
  
  return {hours, minutes, seconds};
}

function pad(time) {
  return time.toString().padStart(2, '0');
}

startTimer();
<div id="output" class="timerClock" value="00:00:00">00:00:00</div>

1 DerekWang Sep 29 2020 at 20:49

È possibile utilizzare String.padStartper inserire il 0simbolo per i valori digitali.

E invece di chiamare la funzione all'interno setTimeoutdi recursive, puoi usare setInterval. Per cancellare il gestore del tempo, puoi usare clearInterval.

E il secondo parametro di setIntervalè milisecondsvalore, quindi per contare il tempo in secondi, sarà bene impostare 1000 - 1 second.

let Dtime = 0;
setInterval(function() {
  Dtime++;
  const hours = Math.floor(Dtime / 3600).toString();
  const mins = Math.floor((Dtime % 3600) / 60).toString();
  const secs = (Dtime % 60).toString();
  document.getElementById("outputt").innerHTML = hours.padStart(2, '0') + ":" + mins.padStart(2, '0') + ":" + secs.padStart(2, '0');
}, 1000);
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>

epascarello Sep 30 2020 at 01:07

Questo è un po 'eccessivo per quello che stai facendo, ma ha un bel metodo per formattare una stringa temporale. Mostra anche come utilizzare Date.now () per tenere traccia della differenza di orario.

class StopWatch { 

  startTime = 0
  previousDuration = 0
  currentDuration = 0
  timer = null;
  listeners = {
    update: [],
    mark: [],
    reset: []
  }
  marks = []
  
  start() {
    if (this.timer) return;
    this.previousDuration += this.currentDuration;
    this.currentDuration = 0;
    this.startTime = Date.now();
    this.run();
  }
  
  stop() {
    if (!this.timer) return;
    this.setDuration();
    window.clearTimeout(this.timer);
    this.timer = null;
  }
  
  run() {
    this.setDuration();
    this.timer = window.setTimeout(this.run.bind(this), 10);
  }
  
  setDuration () {
    this.currentDuration = Date.now() - this.startTime;
    this.trigger('update');
  }
  
  trigger (evtName) {
    const details = {
      time: this.msToTime(this.currentDuration + this.previousDuration),
      marks: [...this.marks]
    };
    this.listeners[evtName].forEach(cb => cb(details));
  }
  
  mark () {
    this.marks.push(this.msToTime(this.currentDuration + this.previousDuration));
    this.trigger('mark');
  }
  
  reset() {
    if (this.timer) window.clearTimeout(this.timer);
    this.currentDuration = 0;
    this.previousDuration = 0;
    this.marks = [];
    this.trigger('update');
    this.trigger('reset');
  }
  
  on (event, callback) {
    var arr = this.listeners[event];
    if (!arr) throw(`unknown event ${event}`);
    arr.push(callback);
    return this;
  }
  
  msToTime (duration) {
    let milliseconds = parseInt((duration % 1000));
    let seconds = Math.floor((duration / 1000) % 60);
    let minutes = Math.floor((duration / (1000 * 60)) % 60);
    let hours = Math.floor((duration / (1000 * 60 * 60)) % 24);

    hours = hours.toString().padStart(2, '0');
    minutes = minutes.toString().padStart(2, '0');
    seconds = seconds.toString().padStart(2, '0');
    milliseconds = milliseconds.toString().padStart(3, '0');

    return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
  }
}

const out = document.querySelector("#out");
const list = document.querySelector("#list");

const myStopWatch = new StopWatch();
myStopWatch
  .on('update', function(data) {
    out.textContent = data.time;
  })
  .on('mark', function(data) {
    var li = document.createElement("li");
    li.textContent = data.marks.pop();
    list.appendChild(li);
  }).on('reset', function () {
    list.innerHTML = '';
  });

const wrapper = document.querySelector("#actions");
wrapper.addEventListener("click", function (evt) {
  const action = evt.target.dataset.action;
  myStopWatch[action]();
});
<div id="out"></div>
<div id="actions">
  <button data-action="start">Start</button>
  <button data-action="stop">Stop</button>
  <button data-action="mark">Mark Time</button>
  <button data-action="reset">Reset</button>
</div>

<ol id="list"></ol>