How can i fix the cancelAnimationFrame function - javascript

So I'm trying to recreate a timer like the one in https://cstimer.net/ for the most part my code is working however when I try to reset the timer it does actually reset the time but the function is repeatedly called, I tested this by logging hello to the console how can I get this to stop as I plan for the page to record the timer result. Here is the code i tried to use to stop the reset function from reccuring.
function resetTime(){
minute.innerHTML = '00';
second.innerHTML = '00';
millisecond.innerHTML = '00';
if (millisecond.innerHTML == '00') {
console.log('hello');
cancelAnimationFrame(resetTime);
}
requestAnimationFrame(resetTime);
}
document.addEventListener('keypress', function(event) {
if (event.code === "KeyR") {
requestAnimationFrame(resetTime);
cancelAnimationFrame(timer);
return;
}
});
function timer() {
let ms = Date.now() - initialTime;
let sec = Math.floor(ms / 1000);
ms = ms % 100;
let min = Math.floor(sec / 60);
sec = sec % 60;
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
requestAnimationFrame(timer);
}

Related

is there a way to somehow hold a keypress in js

So what I'm trying to do is to self replicate a Rubik's cube timer like cstimer.net
What I do is detect a spacebar key press and it is meant to start a timer, however it only adds 1 millisecond at a press instead of a continuous timer, what I'm thinking is to find a way to continuously hold the spacebar if the javascript file, how do I fix this?
const minute = document.getElementById("minute");
const second = document.getElementById("second");
const millisecond = document.getElementById("millisecond");
let ms = 0;
let sec = 0;
let min = 0;
const start = () => console.log("start")
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
timeoutId = setTimeout(function() {
ms = parseInt(ms);
sec = parseInt(sec);
min = parseInt(min);
ms++;
if (ms == 100) {
sec = sec + 1;
ms = 0;
}
if (sec == 60) {
min = min + 1;
sec = 0;
}
if (ms < 10) {
ms = '0' + ms;
}
if (sec < 10) {
sec = '0' + sec;
}
if (min < 10) {
min = '0' + min;
}
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
start();
}, 10);
}
});
<span id="minute"></span>
<span id="second"></span>
<span id="millisecond"></span>
setTimeout only triggers the timer function once; instead you should use requestAnimationFrame to update the timer every repaint.
Let's also fix the unreliableness of the timer implementation as mentioned by #gre_gor. Instead of adding to the ms counter each time, store the initial time (via Date.now()) and then calculate the elapsed milliseconds each time.
const minute = document.getElementById("minute");
const second = document.getElementById("second");
const millisecond = document.getElementById("millisecond");
let initialTime;
function timer() {
let ms = Date.now() - initialTime;
let sec = Math.floor(ms / 1000);
ms = ms % 1000;
let min = Math.floor(sec / 60);
sec = sec % 60;
minute.innerHTML = min;
second.innerHTML = sec;
millisecond.innerHTML = ms;
requestAnimationFrame(timer);
}
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
initialTime = Date.now();
requestAnimationFrame(timer);
}
});
<span id="minute"></span>
<span id="second"></span>
<span id="millisecond"></span>
If you need to stop the timer, it's a bit trickier for a rAF loop than for setTimeout - see How to stop a requestAnimationFrame recursion/loop?.
This one is more precise and has animation built in. (You could be even more precise using function like performance.now())
const timer = document.getElementById("timer");
var start_time = null;
var state = "paused";
function draw() {
if (state == "playing") {
var diff = Date.now() - start_time
var time = (new Date(diff).toISOString().slice(11, 23));
timer.innerHTML = time;
}
}
function loop() {
draw();
requestAnimationFrame(loop)
}
loop();
document.addEventListener('keypress', function(event) {
if (event.code === "Space") {
if (state == "paused") {
state = "playing";
start_time = Date.now()
return;
}
if (state == "playing") {
state = "paused";
start_time = null;
return;
}
}
});
#timer {
font-size:48px;
}
Press [space]<br>
<span id="timer"></span>

How to use a setTimeout function with speech recognition (to stop speech recognition after X minutes and then start again after a 10 second pause)?

Summary of my program:
The user speaks to the computer for 8 minutes (there is a countdown function too) - a button is clicked to initiate speech recognition and the countdown timer
When 8 minutes is reached, speech recognition stops
There is a 10 second delay/pause and then speech recognition continues again (without the user needing to initiate speech recognition by themselves) and the user speaks to the computer for another 2 minutes (once minutes and seconds == 0, speech recognition stops again).
My code:
//HTML STUFF
<html>
<button id="speak_button">Click</button> //button that when clicked triggers speech recognition
<div id="circle"><p id="countdown">8:00</p></div> // circle showing countdown from 8 minutes
</html>
//SPEECH RECOGNITION STUFF
<script>
let isListening = false;
window.addEventListener('DOMContentLoaded', function() {
document.getElementById("speak_button").addEventListener('click', function() {
if (isListening) {
console.log('STOPPING RECOGNITION');
isListening = false;
recognition.stop();
} else {
console.log('STARTING RECOGNITION');
recognition.start();
setInterval(updateCountDown,1000); //So the updateCountDown function occurs 1 second after clicking
updateCountDown();
isListening = true;
}
});
});
//COUNTDOWN FUNCTION
const startingMinutes = 8;
let time = startingMinutes * 60;
function updateCountDown() {
document.getElementById("countdown").innerHTML = "8:00";
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 8 ? '0' + seconds : seconds;
document.getElementById("countdown").innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
if (minutes == 0 && seconds == 0) {
recognition.abort();
isListening = false;
}
};
<script>
Once the countdown timer reaches 0 (once the 8 minutes is complete), I want there to be a 10 second pause for the user and then they have a further 2 minutes of speaking! I tried using a setTimeout function both inside and outside the if statement below but that doesn't seem to work!
function updateCountDown() {
document.getElementById("countdown").innerHTML = "8:00";
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 8 ? '0' + seconds : seconds;
document.getElementById("countdown").innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
if (minutes == 0 && seconds == 0) {
recognition.abort();
isListening = false;
setTimeout(updateCountDownNew, 10,000); //SET TIMEOUT FUNCTION TRIAL
//updateCountDownNew is the same function as updateCountDown but runs for 2 minutes!
}
};
//updateCountDownNew function is below
function updateCountDownNew() {
const startingMinutes = 2;
let time = startingMinutes * 60;
isListening = true;
const minutes = Math.floor(time / 60);
let seconds = time % 60;
seconds = seconds < 2 ? '0' + seconds : seconds;
document.getElementById("countdown").innerHTML = `${minutes}:${seconds}`;
time--;
time = time < 0 ? 0 : time;
if (minutes == 0 && seconds == 0) {
recognition.abort();
isListening = false;
}
};
If anyone has any advice that would be great!

Javascript Counter Troubleshooting

Working on making a 2 minute timer in JS.
I'm new so please bear with me.
I want to make the timer so that when you click the button (I already made the button in js), it decrements the time from 2 minutes. Here is what I have so far. The code doesn't go down second by second. Any suggestions would be helpful, thank you!
const startingMinutes = 2
let time = startingMinutes * 60
let timerId = setInterval(countDown, 1000)
function countDown() {
const minutes = Math.floor(time / 60)
let seconds = time % 60
time--
if (timerId <= -startingMinutes) {
countdown.innerHTMl = 'Good Luck!'
clearInterval(timerId)
}
if (timerId <= 0) {
countdown.innerHTML = 'Time is up!'
clearInterval(timerId)
}
button.addEventListener('click', () => {
countDown()
countdown.innerHTML = minutes + ' minutes ' + ': ' + seconds + ' seconds '
})
}
timerId = setInterval(countDown, 1000)
This work successfully:
const startingMinutes = 0.25
let time = startingMinutes * 60
let timerId = setInterval(countDown, 1000)
function countDown() {
const minutes = Math.floor(time / 60)
let seconds = time % 60
time--
console.log(minutes, 'minutes:', seconds, 'seconds');
if (time <= 0) {
console.log('Time is up!');
clearInterval(timerId)
}
}
Then, just call setInterval with countDown function in your click handler

How would I get this stopwatch to continue running after stop-starting instead of resetting to 00:00:00 upon stop-starting?

I have a stopwatch for a small tool here, I received most of the code from a previous question and I was going about trying to implement it, I began breaking it down and trying to understand it.
So far I think I understand most of it (Still some bits I am researching); however I was trying to adapt the code to my tool.
My requirements:
A start/stop button (a single button) - the value will change depending on if the timer is running or not.
A reset button - this will simply reset the timer to 00:00:00 and if the tool is running it will also stop it.
So far, the reset button is not configured, this is fine. The start and stop button works; however say I stopped the timer, and then started it again without resetting it, the timer just begins at 00:00:00 again, it will not continue from where it was paused.
It would be greatly appreciated if anyone would be able to explain how I could do this? I have tried the following:
Storing 'differenceInMillis for each loop of updateTimer() in a global variable, then subtracting the value from startTime = Date.now() each time the timer is restarted (This was suggested by a user in a previous question), I could not get this to work.
The code I have so far -
HTML (buttons and clock):
const outputElement = document.getElementById("outputt");
var startTime = 0;
var running = 0;
var splitcounter = 0;
function startstop() {
if (running == 0) {
running = 1;
startTime = Date.now();
startstopbutton.value = 'Stop';
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
updateTimer();
} else {
running = 0;
// logTime();
startstopbutton.value = 'Start';
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
function updateTimer() {
if (running == 1) {
let differenceInMillis = Date.now() - startTime;
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
requestAnimationFrame(updateTimer);
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst" id='btnRst' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
UPDATE new version - does not work however - session storage does not work at SO so will have to test elsewhere
const outputElement = document.getElementById("outputt");
var startTime = 0;
var running = 0;
var splitcounter = 0;
function startstop() {
if (running == 0) {
running = 1;
startTime = new Date(sessionStorage.getItem("time"))
if (isNaN(startTime)) startTime = Date.now();
startstopbutton.value = 'Stop';
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
updateTimer();
} else {
running = 0;
logTime();
startstopbutton.value = 'Start';
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
function updateTimer() {
if (running == 1) {
let differenceInMillis = Date.now() - startTime;
sessionStorage.setItem("time", differenceInMillis)
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
requestAnimationFrame(updateTimer);
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
If you MUST use a date object, you will need to change
startTime = Date.now();
to
startTime = new Date(sessionStorage.getItem("time"))
if (isNaN(startTime)) startTime = Date.now();
and save the time
let differenceInMillis = Date.now() - startTime;
sessionStorage.setItem("time",differenceInMillis)
If not, use a counter instead of a date object
Also when you use a toggle as boolean, make it and use it as a boolean (good practice - not mandatory for this issue)
const outputElement = document.getElementById("outputt");
let counter = 0,
running = false,
splitcounter = 0,
lastTime = 0;
function startstop() {
running = !running;
startstopbutton.value = running ? 'Stop' : 'Start';
document.getElementById("outputt").style.backgroundColor = running ? "#2DB37B" : "#B3321B";
if (running) updateTimer(0)
}
function updateTimer(currentTime) {
if (running) requestAnimationFrame(updateTimer)
if (currentTime >= (lastTime + 1000)) {
counter++;
lastTime = currentTime;
let {
hours,
minutes,
seconds
} = calculateTime(counter * 1000);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst" id='btnRst' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>

Countdown Timer not Stopping with clearInterval()

I'm having a problem get this countdown timer to stop at zero so the time won't show as a negative value. The console.log is getting called and works fine but for some reason the clearInterval() is not. This is driving me crazy and I'm close to quitting.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}
<div id="timerContainer"></div>
You are not saving the result of setInterval(timer, 1000);
you should use this:
let timerId;
timer();
timerId = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timerId);
console.log("time's up", diff)
};
As you might see, the result of setInterval is a number (object in node), and all you then need to do is pass that value to clearInterval thus we save the value in the variable timerId for reference.
Don't pass the function that you want stopped to clearInterval().
Pass a reference to the timer that you started, so you need to make sure that when you start a timer, you capture a reference to the ID that will be returned from it.
// Function that the timer will invoke
function callback(){
. . .
}
// Set up and initiate a timer and capture a reference to its unique ID
var timerID = setInterval(callback, 1000);
// When needed, cancel the timer by passing the reference to it
clearInterval(timerID);
The code is fixed make sure you fix your submit button code.
You should first assign the value of setInterval to a variable. That variable is used while calling clearInterval which infact clears the interval.
const timerContainer = document.getElementById('timerContainer');
const THREEMINUTES = 60 * 0.1;//5 seconds for testing
startTimer(THREEMINUTES, timerContainer);
var timer = null;
function startTimer(duration, display) {
let start = Date.now();
let diff, min, sec;
let timer = () => {
diff = duration - (((Date.now() - start) / 1000) | 0);
//use bitwise to truncate the float
min = (diff / 60) | 0;
sec = (diff % 60) | 0;
min = min < 10 ? '0' + min : min;
sec = sec < 10 ? '0' + sec : sec;
display.textContent = min + ':' + sec;
if (diff <= 0) {
stopTimer();
submit.disabled = 'true';
};
};
//call timer immediately otherwise we wait a full second
timer();
timer = setInterval(timer, 1000);
function stopTimer() {
clearInterval(timer);
console.log("time's up", diff);
};
}

Categories

Resources