Iām trying to fluidify a circle progress bar in a timer. The progress bar represents the progression in the duration of the timer. I made an svg <circle>
circle element for the progress bar. Every 10th of second, I change the css attribute stroke-dashoffset
of the circle. This works fine but if we choose less than 5 minutes for the time, the movements of the progress bar are not fluid. What can I do to make that fluid ?
class Timer {
constructor(circle, text) {
this.circle = circle
this.text = text
this.text.innerHTML
}
async start(hours, minutes, seconds) {
this.circle.style["stroke-dasharray"] = parseInt(Math.PI * this.circle.getBoundingClientRect().width)
this.circle.style["stroke-dashoffset"] = parseInt(Math.PI * this.circle.getBoundingClientRect().width)
await this.countdown()
this.circle.classList = "progress"
var remaining, interval, duration, end;
duration = parseInt(hours) * 3600000 + parseInt(minutes) * 60000 + (parseInt(seconds) + 1) * 1000
end = Date.now() + duration
interval = setInterval(async () => {
remaining = end - Date.now()
this.circle.style["stroke-dashoffset"] = remaining * parseInt(Math.PI * this.circle.getBoundingClientRect().width) / duration;
if (remaining < 0) {
this.circle.style["stroke-dashoffset"] = 0
clearInterval(interval)
window.location.href = "./"
return true
} else {
this.text.innerHTML = `${("0" + parseInt(remaining / 3600000)).slice(-2)}:${("0" + parseInt(remaining % 3600000 / 60000)).slice(-2)}:${("0" + parseInt(remaining % 3600000 % 60000 / 1000)).slice(-2)}`
}
}, 100)
}
countdown() {
var duration;
duration = 2
this.text.innerHTML = 3
return new Promise(resolve => {
setInterval(async () => {
if (duration <= 0) {
resolve(true)
} else {
this.text.innerHTML = duration
duration -= 1
}
}, 1000)
})
}
}
const timer = new Timer(document.getElementById("progress"), document.getElementById("text"))
const params = new URLSearchParams(window.location.search)
timer.start(0, 0, 10)
:root {
--pi: 3.141592653589793
}
circle.progress {
display: block;
position: absolute;
fill: none;
stroke: url(#circle.progress.color);
stroke-width: 4.5vmin;
stroke-linecap: round;
transform-origin: center;
transform: rotate(-90deg);
}
circle.progress.animation {
animation: circle 3s linear forwards;
}
.progress-container {
left: 50vw;
top: 50vh;
width: 90vmin;
height: 90vmin;
margin-top: -45vmin;
margin-left: -45vmin;
position: absolute;
padding: none;
}
.outer {
margin: none;
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 6px 6px 10px -1px rgba(0, 0, 0, 0.15), -6px -6px 10px -1px rgba(255, 255, 255, 0.7);
padding: 2.5%;
}
.inner {
margin: 2.5%;
width: 95%;
height: 95%;
border-radius: 50%;
box-shadow: inset 4px 4px 6px -1px rgba(0, 0, 0, 0.15), inset -4px -4px 6px -1px rgba(255, 255, 255, 0.7);
}
svg {
display: block;
position: absolute;
left: 50vw;
top: 50vh;
width: 90.5vmin;
height: 90.5vmin;
margin-top: -45.25vmin;
margin-left: -45.25vmin;
}
svg text {
font-size: 10vmin;
font-family: 'Roboto', sans-serif;
}
@keyframes circle {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: calc(45.5vmin * var(--pi) * 2);
}
}
<link href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@1.2.1/dist/css/materialize.min.css" rel="stylesheet"/>
<div class="progress-container">
<div class="outer center-align">
<div class="inner"></div>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" class="center" version="1.1">
<defs>
<linearGradient id="circle.progress.color">
<stop offset="0%" stop-color="BlueViolet" />
<stop offset="100%" stop-color="MediumVioletRed" />
</linearGradient>
</defs>
<circle id="progress" class="progress animation" cy="45.25vmin" cx="45.25vmin" r="42.75vmin" />
<text id="text" text-anchor="middle" x="50%" y="50%">Temps restant</text>
</svg>
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@1.2.1/dist/js/materialize.min.js"></script>
The code here runs the timer for 10 seconds. Normally, you would have to choose the time in another page. To have the time input, go to that page (the page is in french).