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;
function logTime() {
console.log('Time: ', lastTime)
}
function resetclock() {
running = false;
startTime = 0;
printTime(Date.now())
applyStyles(true)
}
function applyStyles(isReset) {
startstopbutton.value = running ? 'Stop' : 'Start';
document.getElementById("outputt").classList.remove('red', 'green')
if (!isReset) {
document.getElementById("outputt").classList.add(running ? 'red' : 'green')
}
}
function startstop() {
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();
}
}
function printTime(startTime) {
let differenceInMillis = Date.now() - startTime;
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
}
function updateTimer(startTime) {
if (running == true) {
printTime(startTime)
requestAnimationFrame(() => updateTimer(startTime));
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 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
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
.red {
background-color: #2DB37B
}
.green {
background-color: #B3321B
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst1" id='btnRst1' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
Solution 2:
simple stopwatch example
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<input class="startstop" style="width: 120px;" type="button" value="Start" onclick="startstop();">
<input class="reset" style="width: 120px;" type="button" value="Reset" onclick="reset();"/>
<div class="timerClock" value="00:00:00">00:00:00</div>
<script type="text/javascript">
var second = 0
var minute = 0
var hour = 0
var interval
var status = false
var element = document.querySelector('.startstop')
var clock = document.querySelector('.timerClock')
var string = ''
function startstop()
{
if(status == 'false')
{
element.value = 'Stop'
clock.style.backgroundColor = "#2DB37B";
status = true
interval = setInterval(function()
{
string = ''
second += 1
if(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 = false
clearInterval(interval)
}
}
function reset()
{
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;
function startstop() {
const PAUSED = 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);
}
function updateTimerClass(paused) {
var outputClass = 'red';
var buttonText = 'Start';
if (paused) {
outputClass = 'green';
buttonText = 'Stop';
}
startstopbutton.value = buttonText;
outputElement.classList = outputClass;
}
function updateTimer() {
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);
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 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
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
function resetclock() {
let paused = animationId == 0;
startTime = Date.now();
diffTime = 0;
if (paused) {
const REMOVE_ALL_CLASSES = '';
outputElement.className = REMOVE_ALL_CLASSES;
outputElement.innerText = '00:00:00';
}
}
#outputt.green {
background-color: #2DB37B;
}
#outputt.red {
background-color: #B3321B;
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst1" id='btnRst1' value="Reset" onclick="resetclock();"/>
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
Solution 4:
class Stopwatch {
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 ms
this.times[2] += diff / 1000;
// Seconds are 100 hundredths of a second
if (this.times[2] >= 100) {
this.times[1] += 1;
this.times[2] -= 100;
}
// Minutes are 60 seconds
if (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)}`;
}
}
function pad0(value, count) {
var result = value.toString();
for (; result.length < count; --count)
result = '0' + result;
return result;
}
function clearChildren(node) {
while (node.lastChild)
node.removeChild(node.lastChild);
}
let stopwatch = new Stopwatch(
document.querySelector('.stopwatch'),
document.querySelector('.results'));
<input type="button" id="ctrl" value="start" onClick="stopwatch.click();">
<input type="button" value="Reset" onClick="stopwatch.resets();">
<div id="outputt" class="stopwatch"></div>
Post a Comment for "How Can I Fix The Stop-start Process Within This Javascript Stopwatch-clock?"