如何在setInterval中保持类上下文并修复倒计时暂停功能?
点击进度圈无法暂停倒计时的问题解决
问题背景
我实现了一个可倒计时的加载进度圈组件,完成倒计时后会执行指定回调。点击圆圈本应暂停倒计时,但当前实现无效——断点显示suspendCounter方法能触发,但后续setInterval仍忽略pauseCounter的变更。推测是类上下文绑定问题导致修改无法同步,不想使用全局变量,求可行解决方案。
相关代码
/** * 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; } }
解决方案
修改#setStyle方法中的点击事件绑定代码:
// 原代码 this.circle.onclick = this.suspendCounter; // 修改后 this.circle.onclick = () => this.suspendCounter();
原理说明
直接将类方法suspendCounter赋值给onclick时,事件触发时的this会指向触发事件的DOM元素(即circle元素),而非IntervalProgressCircle的实例。此时在suspendCounter中修改的this.pauseCounter是DOM元素的属性,完全不影响类实例的pauseCounter,所以setInterval回调里的判断逻辑自然不会生效。
使用箭头函数包裹后,箭头函数不会创建自己的this上下文,而是继承外部作用域的this(也就是当前类实例)。调用suspendCounter时,方法内部的this正确指向类实例,修改的是实例的pauseCounter属性,setInterval回调就能读到更新后的值,从而实现点击暂停的功能。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

