I have built the following loading spinner that counts a given number of seconds down and then executes some given callbacks. I did this for learning purposes and because I did not want to use another dependency.
When I click the circle, it should stop counting down. But the way it is implemented currently, it won't work. I if I add a break point to suspendCounter()
I can see that we jump into it, and it does it thing but the next time the interval counts down the value is ignored.
I believe this is because my class-context gets "copied" when I execute setInterval()
the first time, and thous any later changes are not passed, and it does not know about them.
But what can I do to make it work? I can set a new property to global
and use that (which works well, btw) but this is surely frowned upon.
/**
* class for creating a progress circle that is counting down
* some callbacks can be passed to the constructor
*/
export class IntervalProgressCircle {
#intervalID;
/**
* Creates the progress circle and mounts it to the parent element
* @param {HTMLDivElement} parentEl - the parent element to mount the progress circle to
* @param {number} seconds - the number of seconds the progress circle should take to complete
* @param {function[]} callbacks - an array of functions to call when the progress circle completes
*/
constructor( parentEl, seconds, ...callbacks ) {
this.seconds = seconds;
this.secondsElapsed = 0;
this.callbacks = callbacks;
this.pauseCounter = false;
this.parentEl = parentEl;
this.svgWrap = document.createElementNS(
"http://www.w3.org/2000/svg", 'svg' );
this.circle = document.createElementNS(
"http://www.w3.org/2000/svg", 'circle' );
this.#setStyle();
this.radius = 32;
this.circumference = this.radius * 2 * Math.PI;
this.circle.style.strokeDasharray = `${ this.circumference } ${ this.circumference }`;
this.circle.style.strokeDashoffset = `${ this.circumference }`;
this.svgWrap.appendChild( this.circle );
this.parentEl.appendChild( this.svgWrap );
}
setProgress( percent ) {
this.circle.style.strokeDashoffset = ( this.circumference - percent / 100 * this.circumference ).toString();
}
suspendCounter() {
this.pauseCounter = !this.pauseCounter;
}
startInterval() {
/**
* interval for reloading table data
*/
if ( this.#intervalID ) {
clearInterval( this.#intervalID );
}
this.#intervalID = setInterval( async () => {
if ( this.pauseCounter === true ) return;
if ( this.secondsElapsed > this.seconds ) {
this.secondsElapsed = 1;
await this.callbacks.forEach( cb => cb() );
return;
}
this.setProgress( ( this.secondsElapsed / this.seconds ) * 100 );
this.secondsElapsed += 1;
}, 1000 );
}
#setStyle() {
this.svgWrap.setAttributeNS( null, 'id', 'progress-ring' );
this.svgWrap.setAttributeNS( null, 'class', 'progress-ring' );
this.svgWrap.setAttributeNS( null, 'width', '5em' );
this.svgWrap.setAttributeNS( null, 'height', '5em' );
this.circle.setAttributeNS( null, 'stroke', 'white' );
this.circle.setAttributeNS( null, "stroke-width", "0.4em" )
this.circle.setAttributeNS( null, "fill", "transparent" )
this.circle.setAttributeNS( null, "cx", "2.5em" )
this.circle.setAttributeNS( null, "cy", "2.5em" )
this.circle.setAttributeNS( null, "r", "2em" )
this.circle.setAttributeNS(
null, "style",
"stroke-dasharray: 201.062px, " +
"201.062px; stroke-dashoffset: 174.254px;" +
"transition: 1.45s stroke-dashoffset;" +
"transform: rotate(-90deg);" +
"transform-origin: 50% 50%;"
)
this.circle.onclick = this.suspendCounter;
}
}
SOLUTION
All that needed to change was:
this.circle.onclick = this.suspendCounter;
to
this.circle.onclick = () => this.suspendCounter();