简体   繁体   中英

Javascript setInterval inside object constructor doesn't work as expected

I am learning Javascript objects and I have a very weird issue with my code. The code below makes a clock hour, min, sec arms to move according to system time using setInterval() . I have added a start and stop button to start/stop the clock. But when I run the runClock() inside interval it doesn't work as expected.

It works fine when I place the code in anonymous function inside setInterval like setInterval(function(){... },1000) .

 const HOURHAND = document.querySelector("#hour"); const MINUTEHAND = document.querySelector("#minute"); const SECONDHAND = document.querySelector("#second"); function TimerInit(){ this.timer = null; this.startBtn = document.querySelector('#startBtn'); this.stopBtn = document.querySelector('#stopBtn'); this.init = function(){ const dt = new Date(); console.log(`Hours: ${dt.getHours()} min: ${dt.getMinutes()} sec: ${dt.getSeconds()}`); let hr = dt.getHours(); let min = dt.getMinutes(); let sec = dt.getSeconds(); let hourDeg = (hr * 360 / 12) + (min * (36/60) / 12); let minDeg = (min * 360 / 60) + (sec * (36/60) / 60); let secDeg = sec * 360 / 60; return { hr:hourDeg, min:minDeg, sec:secDeg }; }; this.startBtn.onclick = function(){ let { hr, min, sec } = this.init(); console.log(`${hr} ${min} ${sec}`); this.timer = setInterval(this.runClock.bind(this, hr, min, sec), 1000); //the code below works /*this.timer = setInterval(()=>{ hr += (3/360); min += (6/60); sec += 6; console.log(`${hr} ${min} ${sec}`); HOURHAND.style.transform = `rotate(${hr}deg)`; MINUTEHAND.style.transform = `rotate(${min}deg)`; SECONDHAND.style.transform = `rotate(${sec}deg)`; },1000);*/ }.bind(this); this.runClock = function(hr,min,sec){ hr += (3/360); min += (6/60); sec += 6; //console.log(`${hr} ${min} ${sec}`); HOURHAND.style.transform = `rotate(${hr}deg)`; MINUTEHAND.style.transform = `rotate(${min}deg)`; SECONDHAND.style.transform = `rotate(${sec}deg)`; } this.stopBtn.onclick = function(){ clearInterval(this.timer); }.bind(this); } const obj = new TimerInit();
 /* Layout */.main { display: flex; padding: 2em; height: 90vh; justify-content: center; align-items: middle; }.clockbox, #clock { width: 100%; } /* Clock styles */.circle { fill: none; stroke: #000; stroke-width: 9; stroke-miterlimit: 10; }.mid-circle { fill: #000; }.hour-marks { fill: none; stroke: #000; stroke-width: 9; stroke-miterlimit: 10; }.hour-arm { fill: none; stroke: #000; stroke-width: 17; stroke-miterlimit: 10; }.minute-arm { fill: none; stroke: #000; stroke-width: 11; stroke-miterlimit: 10; }.second-arm { fill: none; stroke: #000; stroke-width: 4; stroke-miterlimit: 10; } /* Transparent box ensuring arms center properly. */.sizing-box { fill: none; } /* Make all arms rotate around the same center point. */ /* Optional: Use transition for animation. */ #hour, #minute, #second { transform-origin: 300px 300px; transition: transform.5s ease-in-out; }
 <main class="main"> <div class="clockbox"> <svg id="clock" xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600"> <g id="face"> <circle class="circle" cx="300" cy="300" r="253.9"/> <path class="hour-marks" d="M300.5 94V61M506 300.5h32M300.5 506v33M94 300.5H60M411.3 107.8l7.9-13.8M493 190.2l13-7.4M492.1 411.4l16.5 9.5M411 492.3l8.9 15.3M189 492.3l-9.2 15.9M107.7 411L93 419.5M107.5 189.3l-17.1-9.9M188.1 108.2l-9-15.6"/> <circle class="mid-circle" cx="300" cy="300" r="16.2"/> </g> <g id="hour"> <path class="hour-arm" d="M300.5 298V142"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> <g id="minute"> <path class="minute-arm" d="M300.5 298V67"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> <g id="second"> <path class="second-arm" d="M300.5 350V55"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> </svg> </div><.-- .clockbox --> </main> <button id="startBtn">Start</button> <button id="stopBtn">Stop</button>

Any opinion is welcome.

setInterval() is not working when you call the named function this.runClock() because every time it is called by setInterval, it receives the same value of hr , min , and sec . When these values are incremented inside the function (to make the clock tick), they are saved nowhere because the variables are lost when the function executes. Then when the function is called again, it again receives the same value of hr , min and sec that it received when it first ran. This way, the clock doesn't work..

This is because runClock doesn't maintain the state of the object. Instead you can save these values in the object's state itself and then use them directly in runClock() . This way, every time they are incremented, that incremented value will be saved.

 const HOURHAND = document.querySelector("#hour"); const MINUTEHAND = document.querySelector("#minute"); const SECONDHAND = document.querySelector("#second"); function TimerInit(){ this.timer = null; this.startBtn = document.querySelector('#startBtn'); this.stopBtn = document.querySelector('#stopBtn'); this.init = function(){ const dt = new Date(); console.log(`Hours: ${dt.getHours()} min: ${dt.getMinutes()} sec: ${dt.getSeconds()}`); let hr = dt.getHours(); let min = dt.getMinutes(); let sec = dt.getSeconds(); this.hourDeg = (hr * 360 / 12) + (min * (36/60) / 12); this.minDeg = (min * 360 / 60) + (sec * (36/60) / 60); this.secDeg = sec * 360 / 60; }; this.startBtn.onclick = function(){ this.init(); this.timer = setInterval(this.runClock.bind(this), 1000); }.bind(this); this.runClock = function(){ this.hourDeg += (3/360); this.minDeg += (6/60); this.secDeg += 6; console.log(`${this.hourDeg} ${this.minDeg} ${this.secDeg}`); HOURHAND.style.transform = `rotate(${this.hourDeg}deg)`; MINUTEHAND.style.transform = `rotate(${this.minDeg}deg)`; SECONDHAND.style.transform = `rotate(${this.secDeg}deg)`; } this.stopBtn.onclick = function(){ clearInterval(this.timer); }.bind(this); } const obj = new TimerInit();
 /* Layout */.main { display: flex; padding: 2em; height: 90vh; justify-content: center; align-items: middle; }.clockbox, #clock { width: 100%; } /* Clock styles */.circle { fill: none; stroke: #000; stroke-width: 9; stroke-miterlimit: 10; }.mid-circle { fill: #000; }.hour-marks { fill: none; stroke: #000; stroke-width: 9; stroke-miterlimit: 10; }.hour-arm { fill: none; stroke: #000; stroke-width: 17; stroke-miterlimit: 10; }.minute-arm { fill: none; stroke: #000; stroke-width: 11; stroke-miterlimit: 10; }.second-arm { fill: none; stroke: #000; stroke-width: 4; stroke-miterlimit: 10; } /* Transparent box ensuring arms center properly. */.sizing-box { fill: none; } /* Make all arms rotate around the same center point. */ /* Optional: Use transition for animation. */ #hour, #minute, #second { transform-origin: 300px 300px; transition: transform.5s ease-in-out; }
 <main class="main"> <div class="clockbox"> <svg id="clock" xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600"> <g id="face"> <circle class="circle" cx="300" cy="300" r="253.9"/> <path class="hour-marks" d="M300.5 94V61M506 300.5h32M300.5 506v33M94 300.5H60M411.3 107.8l7.9-13.8M493 190.2l13-7.4M492.1 411.4l16.5 9.5M411 492.3l8.9 15.3M189 492.3l-9.2 15.9M107.7 411L93 419.5M107.5 189.3l-17.1-9.9M188.1 108.2l-9-15.6"/> <circle class="mid-circle" cx="300" cy="300" r="16.2"/> </g> <g id="hour"> <path class="hour-arm" d="M300.5 298V142"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> <g id="minute"> <path class="minute-arm" d="M300.5 298V67"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> <g id="second"> <path class="second-arm" d="M300.5 350V55"/> <circle class="sizing-box" cx="300" cy="300" r="253.9"/> </g> </svg> </div><.-- .clockbox --> </main> <button id="startBtn">Start</button> <button id="stopBtn">Stop</button>

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