O relógio Javascript passa de 60 para minutos e segundos

Sep 29 2020

Eu fiz um relógio Javascript, quando os segundos e minutos chegarem a 60 ele contará mais de 60 e continuará funcionando ...

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>

Basicamente, o relógio conta depois de 60 para minutos e segundos, gostaria que isso fosse redefinido para 0 em vez de bater 60. Não tive tempo para testar as horas, embora tenha certeza de que passaria de 24 também.

Por favor, alguém pode explicar como isso seria feito? Tenho certeza de que é muito simples, mas tenho conseguido lutar por muito tempo e estou ficando frustrado.

Respostas

2 Ivar Sep 29 2020 at 20:46

Você pode usar o operador de resto ( %) para voltar a 0 quando um número passar de 60.

Por exemplo

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

Portanto, seu código ficaria assim:

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>

Observe que este cronômetro pode variar com o tempo. Para saber por que e como você pode resolver isso, consulte Como criar um cronômetro preciso em javascript?

2 RickardElimää Sep 29 2020 at 21:32

Você não pode confiar que setTimeout ou setInterval serão executados em exatamente X milissegundos. Confira o vídeo a seguir sobre loops de eventos para entender por quê. É algo que você deve saber ao programar javascript.

Em vez disso, o que você precisa fazer é atualizar o elemento de saída toda vez que a tela é redesenhada, o que pode ser feito com requestAnimationFrame . Você precisa armazenar quando o relógio começa, o que pode ser feito em milissegundos, e então calcular a diferença entre a hora de início e a hora atual (com Date.now()) quando a janela está prestes a fazer uma repintura. requestAnimationFramerecebe um retorno de chamada como um parâmetro, que deve ser o método que atualiza seu elemento de saída.

EDIT: Eu acelerei o cronômetro para mostrar o caso do minuto e segundo 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

Você pode usar String.padStartpara inserir 0símbolo para valores digitais.

E em vez de chamar a função dentro de setTimeoutfor recursive, você pode usar setInterval. Para limpar o manipulador de tempo, você pode usar clearInterval.

E o segundo parâmetro de setIntervalé o milisecondsvalor, para contar o tempo por segundos, será bom configurar 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

Isso é um pouco exagerado para o que você está fazendo, mas tem um bom método para formatar uma string de tempo. Ele também mostra como usar Date.now () para controlar a diferença de tempo.

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>