This timer has an animation that uses the html tag and its and svg
attributes . With the first attribute I draw the perimeter of the circumference with blue and with the second I use it to give animation to the timer and graphically describe how long it takes for the timer to reach 0. I would like that when the animation stops it does not start again from the start of the circumference but continue from the point where it left off when clicking the pause button, until completing the remaining time. How can I do it?stroke-dasharray
stroke-dashoffset
class Timer {
constructor(durationInput, startButton, pauseButton, callbacks) {
this.durationInput = durationInput;
this.startButton = startButton;
this.pauseButton = pauseButton;
if (callbacks) {
// Se activan los callbacks
this.onStart = callbacks.onStart;
this.onTick = callbacks.onTick;
this.onComplete = callbacks.onComplete;
}
// Al crear la instancia se esperan los eventos
this.startButton.addEventListener("click", this.start);
this.pauseButton.addEventListener("click", this.pause);
}
start = () => {
if (this.onStart) {
// Se comprueba tiene callback y ejecuta la función
this.onStart(this.timeRemaining);
}
this.tick();
this.interval = setInterval(this.tick, 20); // Guardamos el ID de setInterval
};
pause = () => {
clearInterval(this.interval); // Para la ejecución del timer usando el ID
};
tick = () => {
if (this.timeRemaining <= 0) {
// Pausamos el timer si llego a 0 timeRemaining y ejecutamos onComplete
this.pause();
if (this.onComplete) {
this.onComplete();
}
} else {
this.timeRemaining = this.timeRemaining - 0.02; // Restamos 0.02seg a timeRemaining
if (this.onTick) {
this.onTick(this.timeRemaining); // Ejecutamos onTick para la animación
}
}
};
// Obtenemos en formato flotante el valor del input
get timeRemaining() {
return parseFloat(this.durationInput.value);
}
// Configura el tiempo restante con 2 digitos despues del punto
set timeRemaining(time) {
this.durationInput.value = time.toFixed(2);
}
}
const durationInput = document.querySelector("#duration");
const startButton = document.querySelector("#start");
const pauseButton = document.querySelector("#pause");
const circle = document.querySelector("circle");
const perimeter = circle.getAttribute("r") * 2 * Math.PI;
circle.setAttribute("stroke-dasharray", perimeter);
let duration;
const timer = new Timer(durationInput, startButton, pauseButton, {
onStart(totalDuration) {
duration = totalDuration; // Guardamos el tiempo total cuando el tiempo esta en pausa y se clickea el boton start
},
onTick(timeRemaining) {
// Agrega el atributo a la etiqueta svg cada vez que se ejecuta tick y onTick
circle.setAttribute(
"stroke-dashoffset",
(perimeter * timeRemaining) / duration - perimeter // formula para calcular la medida del atributo que dará el efecto animado
);
},
onComplete() {
console.log("Timer is completed");
},
});
html {
background: url(https://source.unsplash.com/weekly?water);
background-size: cover;
}
* {
font: inherit;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Robot, Oxygen, Ubuntu, Cantarell, "Fira Sans",
"Droid Sans", Helvetica, Arial, sans-serif;
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
.timer {
position: relative;
display: inline-block;
}
.dial {
height: 400px;
width: 400px;
}
.timer input {
display: block;
border: none;
width: 240px;
font-size: 90px;
text-align: center;
background: transparent;
}
.timer button {
border: none;
font-size: 36px;
cursor: pointer;
background-color: inherit;
}
.timer button:focus {
outline: none;
}
.timer input:focus {
outline: none;
}
.controls {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
<link rel="stylesheet" href="style.css" />
<title>Timer Back</title>
</head>
<body>
<div class="timer">
<div class="controls">
<input id="duration" value="3" />
<div>
<button id="start"><i class="fas fa-play"></i></button>
<button id="pause"><i class="fas fa-pause"></i></button>
</div>
</div>
<svg class="dial">
<circle
r="190"
cx="200"
cy="200"
fill="transparent"
stroke="blue"
stroke-width="15"
transform="rotate(-90 200 200)"
></circle>
</svg>
</div>
<script src="timer.js"></script>
<script src="index.js"></script>
</body>
</html>
The problem is that with each click on
start
you reassign the variableduration
and you only have to do that on the first run. When you return from the pause, you simply continue.An evaluation is enough to make it work as you expect: