I'm writing Javascript to play a series of repeated animations that go to the notes of a song that plays in the browser. Here is the code I currently have that works:
//Determine the timing for each note played
var expTimes = [0, 215, 323, 645, 860, 968, 1290...]
var strumTimeout;
//Loop through the array and strum at each listed timeout through the duration of the audio
function StrumPattern(strumArray){
//Toggles the CSS transforms to make the strum animation happen
function strum(){
if (strumHand.style.transform == "rotate(50deg)"){
strumHand.style.transform = "rotate(0deg)";
}else{
strumHand.style.transform = "rotate(50deg)";
}
}
//Sets the timeout for each strum animation to play in a sequence
strumArray.forEach(delay => {
strumTimeout = setTimeout(() => {
strum();
console.log("strum");
}, delay);
});
}
However, I want to be able to clear all these timeouts when the user turns off the music. I was able to make the strumming animation stop by adding an if statement around the strum(); function, but it does not stop the forEach loop from running through the array, and if I play the animation again before the array completes, it gets all jittery because it is running two forEach loops at the same time.
I tried using this function below to target all the setTimeouts that I thought would be labeled strumTimeout from the forEach loop. This does not work though, as it gets rid of ALL setTimeout functions on the page, when I have other setTimeout functions that need to run to stop other animations at different points.
//Clears
function clearStrum(){
var id = strumTimeout;
while (id--) {
console.log('removing timeout');
window.clearTimeout(id); // will do nothing if no timeout with id is present
}
}
Furthermore, the console.log prints out an increasing number of 'removing timeout' strings each time I activate it
I have also tried using a regular for loop and a break, but it either did not work or it said "Jump target cannot cross function boundary," depending on where I put it.
If there is a way to label the setTimeouts created by the forEach loop and clear or remove them somehow, that is what I am looking for. However, if there is a better way to get animations to play in a sequence with variable timeout times or interval times, I'm curious to hear as well.
You can use Array#map
to get all the timeout ids from the loop in an array to be cleared later.
// outside the function
let timeouts;
// inside the function, replace strumArray.forEach with the following:
timeouts = strumArray.map(delay => setTimeout(() => {
strum();
console.log("strum");
}, delay));
// cancel all those timeouts later:
timeouts?.forEach(id => clearTimeout(id));