How Can I Fix The Stop-start Process Within This Javascript Stopwatch-clock?
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?"