Skip to content Skip to sidebar Skip to footer

How Can I Fix The Stop-start Process Within This Javascript Stopwatch-clock?

I have a JavaScript stopwatch here, I require the start-stop button to keep the same time when continuing. Currently, if I stop and continue the clock diff is something ridiculous

Solution 1:

Issue with your code:

  • You start with initial value for sessionStorage as Date.now but then save difference on update.
  • You interact a lot with session storage. Any communication with external API is expensive. Instead use local variables and find an event to initialise values.
  • Time difference logic is a bit off.
    • Date.now - startTime does not considers the difference between stop action and start action.
    • You can use this logic: If startTime is defined, calculate difference and add it to start time. If not, initialise it to Date.now()

Suggestions:

  • Instead of adding styles, use classes. That will help you in reset functionality
  • Define small features and based on it, define small functions. That would make reusability easy
  • Try to make functions independent by passing arguments and only rely on them. That way you'll reduce side-effect

Note: as SO does not allow access to Session Storage, I have removed all the related code.

const outputElement = document.getElementById("outputt");
var running = false;
var splitcounter = 0;
var lastTime = 0;
var startTime = 0;

functionlogTime() {
  console.log('Time: ', lastTime)
}

functionresetclock() {
  running = false;
  startTime = 0;
  printTime(Date.now())
  applyStyles(true)
}

functionapplyStyles(isReset) {
  startstopbutton.value = running ? 'Stop' : 'Start';
  document.getElementById("outputt").classList.remove('red', 'green')
  if (!isReset) {
    document.getElementById("outputt").classList.add(running ? 'red' : 'green')
  }
}
functionstartstop() {
  running = !running;
  applyStyles();
  if (running) {
    if (startTime) {
      const diff = Date.now() - lastTime;
      startTime = startTime + diff;
    } else {
      startTime = Date.now()
    }
    
    updateTimer(startTime);
  } else {
    lastTime = Date.now()
    logTime();
  }
}

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

functionupdateTimer(startTime) {
  if (running == true) {
    printTime(startTime)
    requestAnimationFrame(() =>updateTimer(startTime));
  }
}

functioncalculateTime(milliS) {
  constSECONDS = 1000; // should be 1000 - only 10 to speed up the timerconstMINUTES = 60;
  constHOURS = 60;
  constRESET = 60;

  let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
  let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
  let seconds = Math.floor(milliS / SECONDS) % RESET;

  return {
    hours,
    minutes,
    seconds
  };
}

functionpad(time) {
  return time.toString().padStart(2, '0');
}
.red {
  background-color: #2DB37B
}

.green {
  background-color: #B3321B
}
<inputid="startstopbutton"class="buttonZ"style="width: 120px;"type="button"name="btn"value="Start"onclick="startstop();"><inputid="resetbutton"class="buttonZ"style="width: 120px;"type="button"name="btnRst1"id='btnRst1'value="Reset"onclick="resetclock();" /><divid="outputt"class="timerClock"value="00:00:00">00:00:00</div>

Solution 2:

simple stopwatch example

<!DOCTYPE html><html><head><title></title></head><body><inputclass="startstop"style="width: 120px;"type="button"value="Start"onclick="startstop();"><inputclass="reset"style="width: 120px;"type="button"value="Reset"onclick="reset();"/><divclass="timerClock"value="00:00:00">00:00:00</div><scripttype="text/javascript">var second = 0var minute = 0var hour = 0var interval
    var status = falsevar element = document.querySelector('.startstop')
    var clock = document.querySelector('.timerClock')
    var string = ''functionstartstop() 
    {
        if(status == 'false')
        {
            element.value = 'Stop'
            clock.style.backgroundColor = "#2DB37B";
            status = true
            interval = setInterval(function()
            {
                string = ''
                second += 1if(second >= 60)
                {
                    minute += 1
                    second = 0
                }
                if(minute >= 60)
                {
                    hour += 1
                    minute = 0
                }

                if(hour < 10)
                    string += `0${hour}:`else
                    string += `${hour}:`if(minute < 10)
                    string += `0${minute}:`else
                    string += `${minute}:`if(second < 10)
                    string += `0${second}`else
                    string += `${second}`

                clock.innerHTML = string
            },1000)         
        }
        else
        {
            clock.style.backgroundColor = "#B3321B";
            element.value = 'Start'
            status = falseclearInterval(interval)
        }
    }
    functionreset()
    {
        second = 0
        minute = 0
        hour = 0
        status = false
        element.value = 'Start'clearInterval(interval)
        clock.innerHTML = `00:00:00`
        clock.style.backgroundColor = "transparent";
    }
</script></body></html>

Solution 3:

One thing to know about requestAnimationFrame is that it returns an integer that is a reference to the next animation. You can use this to cancel the next waiting animation with cancelAnimationFrame.

As mentioned by @Rajesh, you shouldn't store the time each update, as it will stop the current process for a (very) short while. Better in that case to fire an event, preferably each second, that will wait until it can run. I haven't updated the code to take that into account, I only commented it away for now.

It's also better to use classes than updating element styles. I wrote sloppy code that overwrites all classes on the #outputt element (it's spelled "output"). That's bad programming, because it makes it impossible to add other classes, but it serves the purpose for now. @Rajesh code is better written for this purpose.

I added two variables - diffTime and animationId. The first one corrects startTime if the user pauses. The second one keeps track if there is an ongoing timer animation.

I refactored your style updates into a method of its own. You should check it out, because it defines standard values and then changes them with an if statement. It's less code than having to type document.getElementById("outputt").style... on different rows.

I also added a resetclock method.

const outputElement = document.getElementById("outputt");
var startTime = 0;
var diffTime = 0;
var animationId = 0;

functionstartstop() {
  constPAUSED = 0;
  let paused = animationId == PAUSED;
  //diffTime = new Date(sessionStorage.getItem("time")) || 0;
  startTime = Date.now() - diffTime;

  if (paused) {
    updateTimer();
  } else {
    cancelAnimationFrame(animationId);
    animationId = PAUSED;
  }  

  updateTimerClass(paused);
}

functionupdateTimerClass(paused) {
  var outputClass = 'red';
  var buttonText = 'Start';
  
  if (paused) {
    outputClass = 'green';
    buttonText = 'Stop';
  } 
  
  startstopbutton.value = buttonText;
  outputElement.classList = outputClass;
}

functionupdateTimer() {
  let differenceInMillis = Date.now() - startTime;
  //sessionStorage.setItem("time", differenceInMillis)let {
    hours,
    minutes,
    seconds
  } = calculateTime(differenceInMillis);
  let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;

  outputElement.innerText = timeStr;
  diffTime = differenceInMillis;
  animationId = requestAnimationFrame(updateTimer);
}

functioncalculateTime(milliS) {
  constSECONDS = 1000; // should be 1000 - only 10 to speed up the timerconstMINUTES = 60;
  constHOURS = 60;
  constRESET = 60;

  let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
  let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
  let seconds = Math.floor(milliS / SECONDS) % RESET;

  return {
    hours,
    minutes,
    seconds
  };
}

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

functionresetclock() {
  let paused = animationId == 0;
  
  startTime = Date.now();
  diffTime = 0;
  
  if (paused) {
    constREMOVE_ALL_CLASSES = '';
    outputElement.className = REMOVE_ALL_CLASSES;
    outputElement.innerText = '00:00:00';
  }
}
#outputt.green {
  background-color: #2DB37B;
}

#outputt.red {
  background-color: #B3321B;
}
<inputid="startstopbutton"class="buttonZ"style="width: 120px;"type="button"name="btn"value="Start"onclick="startstop();"><inputid="resetbutton"class="buttonZ"style="width: 120px;"type="button"name="btnRst1"id='btnRst1'value="Reset"onclick="resetclock();"/><divid="outputt"class="timerClock"value="00:00:00">00:00:00</div>

Solution 4:

classStopwatch {
    constructor(display, results) {
        this.running = false;
        this.display = display;
        this.results = results;
        this.laps = [];
        this.reset();
        this.print(this.times);
    }
    
    reset() {
        this.times = [ 0, 0, 0 ];
    }
  click(){
    var x=document.getElementById('ctrl');
    if(x.value=="start"){
     this.start();
     x.value="stop";
     document.getElementById("outputt").style.backgroundColor = "#2DB37B";
    }
    else{
     x.value="start";
     this.stop();
     document.getElementById("outputt").style.backgroundColor = "#B3321B";
    }
  }
    start() {
        if (!this.time) this.time = performance.now();
        if (!this.running) {
            this.running = true;
            requestAnimationFrame(this.step.bind(this));
        }
    }
    
    stop() {
        this.running = false;
        this.time = null;
    }

    resets() {
        document.getElementById("outputt").style.backgroundColor = "#2DB37B";
        if (!this.time) this.time = performance.now();
        if (!this.running) {
            this.running = true;
            requestAnimationFrame(this.step.bind(this));
        }
        this.reset();
    }
    
    
    step(timestamp) {
        if (!this.running) return;
        this.calculate(timestamp);
        this.time = timestamp;
        this.print();
        requestAnimationFrame(this.step.bind(this));
    }
    
    calculate(timestamp) {
        var diff = timestamp - this.time;
        // Hundredths of a second are 100 msthis.times[2] += diff / 1000;
        // Seconds are 100 hundredths of a secondif (this.times[2] >= 100) {
            this.times[1] += 1;
            this.times[2] -= 100;
        }
        // Minutes are 60 secondsif (this.times[1] >= 60) {
            this.times[0] += 1;
            this.times[1] -= 60;
        }
    }
    
    print() {
        this.display.innerText = this.format(this.times);
    }
    
    format(times) {
        return`\
${pad0(times[0], 2)}:\
${pad0(times[1], 2)}:\
${pad0(Math.floor(times[2]), 2)}`;
    }
}

functionpad0(value, count) {
    var result = value.toString();
    for (; result.length < count; --count)
        result = '0' + result;
    return result;
}

functionclearChildren(node) {
    while (node.lastChild)
        node.removeChild(node.lastChild);
}

let stopwatch = newStopwatch(
    document.querySelector('.stopwatch'),
    document.querySelector('.results'));
<inputtype="button"id="ctrl"value="start"onClick="stopwatch.click();"><inputtype="button"value="Reset"onClick="stopwatch.resets();"><divid="outputt"class="stopwatch"></div>

Post a Comment for "How Can I Fix The Stop-start Process Within This Javascript Stopwatch-clock?"