Reputation: 11
I created an app where users can input time, and a timer and clockhand will countdown in unison.
When I pause then resume, however, the timer UI works as anticipated, but the clock resets back to its resting position instead of continuing from its paused position.
The user should be able to input any time, and the clockhand should reflect the difference between the time entered and the time remaining--actinig like a progress bar, running from 100% to 0%.
How can I fix this pause + resume behavior in the clock?
JavaScript MVP
let remaining = duration
let timer = null
let end = 0
// Start button
startButton.addEventListener('click', e => {
// Continue if timer is running
if (timer) return
duration = (hoursEntered * 3600000) + (minutesEntered * 60000) + (secondsEntered * 1000);
remaining = duration
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() => {
// If remainisng time is zero, stop clock
if (end - Date.now() <= 16) {
clearInterval(timer);
timer = 0;
showStartButton()
} else {
render(end - Date.now())
animateRing();
}
}, 16)
})
// Pause button
pauseButton.addEventListener('click', e => {
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Note the remaining time
remaining = end - Date.now()
render(remaining)
})
// Divide time left by time remaining
const calcPercent = () => {
return percent = (end - Date.now()) / remaining;
};
// Update the ring and clockhand as time passes, starting with 283
// Where the length of arc = 2πr = 2 * π * 45 = 282.6
const animateRing = () => {
const ringArray = `${(calcPercent() * ring).toFixed(0)} 283`;
document.getElementById('base-timer-path-remaining').setAttribute('stroke-dasharray', ringArray);
// Animate clockhand
document.getElementById('clockhand').style.transform = 'rotate(-' + (calcPercent() * 360) + 'deg)';
};
render(remaining)
Upvotes: 0
Views: 410
Reputation: 370
calcPercent was always 1 when you hit the start button. You were dividing the remaining time by the remaining time (end = Date.now() + remaining is the same as end - Date.now() = remaining). You have several equivalent expressions in your algorithm, I suggest a rewrite. However, the code below is functioning the way you want, I tested in your fiddle.
// Get inputs and buttons
const hoursInput = document.getElementById('hours')
const minutesInput = document.getElementById('minutes')
const secondsInput = document.getElementById('seconds')
const startButton = document.getElementById('start')
const pauseButton = document.getElementById('pause')
const resetButton = document.getElementById('reset')
var startSeconds=900000;
// Leading zeroes for timer UI
const pad = v => `00${v}`.slice(-2)
// Compute units from milliseconds
const hours = m => Math.floor((m / 3600000) % 24)
const minutes = m => Math.floor((m / 60000) % 60)
const seconds = m => Math.floor((m / 1000) % 60)
// Render function
const render = v => {
hoursInput.value = pad(hours(v))
minutesInput.value = pad(minutes(v))
secondsInput.value = pad(seconds(v))
const hoursEntered = document.getElementById('hours').value;
const minutesEntered = document.getElementById('minutes').value;
const secondsEntered = document.getElementById('seconds').value;
duration = (hoursEntered * 3600000) + (minutesEntered * 60000) + (secondsEntered * 1000);
remaining = duration
}
// Length of arc
const ring = 283;
// Set initial value to 15 minutes
let duration = 900000
let remaining = duration
let timer = null
let end = 0
// Start button
startButton.addEventListener('click', e => {
// Continue if timer is running
if (timer) return
// Show/hide start and pause
showPauseButton()
// Convert user input to milliseconds
const hoursEntered = document.getElementById('hours').value;
const minutesEntered = document.getElementById('minutes').value;
const secondsEntered = document.getElementById('seconds').value;
duration = (hoursEntered * 3600000) + (minutesEntered * 60000) + (secondsEntered * 1000);
remaining = duration
// Get end timestamp based on current time + remaining time
end = Date.now() + remaining
// Render the remaining time every 16ms (approx 60fps)
timer = setInterval(() => {
// If remainisng time is zero, stop clock
if (end - Date.now() <= 16) {
clearInterval(timer);
timer = 0;
showStartButton()
} else {
render(end - Date.now())
animateRing();
}
}, 16)
})
// Pause button
pauseButton.addEventListener('click', e => {
// Do nothing if timer not running
if(!timer) return
// Otherwise, clear timer
clearInterval(timer)
timer = null
// Show/hide start and pause
showStartButton()
// Note the remaining time
remaining = end - Date.now()
render(remaining)
})
// Reset button
resetButton.addEventListener('click', e => {
// Show/hide start and pause
showStartButton()
// Clear timer
clearInterval(timer)
timer = null
// Reset ring
document.getElementById('base-timer-path-remaining').setAttribute('stroke-dasharray', '283 283');
document.getElementById('clockhand').style.transform = 'rotate(-360deg)';
// Reset remaining to original duration
remaining = 900000
render(remaining)
})
// Show pause button, hide start button
const showPauseButton = () => {
document.getElementById('pause').style.display = 'flex';
document.getElementById('start').style.display = 'none';
};
// Show start button, hide pause button
const showStartButton = () => {
document.getElementById('pause').style.display = 'none';
document.getElementById('start').style.display = 'flex';
};
// Divide time left by time remaining
const calcPercent = () => {
return percent = remaining/startSeconds;
};
// Update the ring and clockhand as time passes, starting with 283
// Where the length of arc = 2πr = 2 * π * 45 = 282.6
const animateRing = () => {
const ringArray = `${(calcPercent() * ring).toFixed(0)} 283`;
document.getElementById('base-timer-path-remaining').setAttribute('stroke-dasharray', ringArray);
// Animate clockhand
document.getElementById('clockhand').style.transform = 'rotate(-' + (calcPercent() * 360) + 'deg)';
};
render(remaining)
Upvotes: 1