简体   繁体   中英

Pause & resume JavaScript timer with clock

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?

The Fiddle

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)

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM