I am writing a program that visualizes mergesort in react. I want to allow the user to use a slider to change the sorting speed. I am using a setTimeout function within a for loop in order to schedule the iterations are equally spaced time intervals. I would like to somehow have the value obtained from the slider to be reflected in the setTimeout however, as the loop executes immediately and schedules the iterations of the loop, the delay value is calculated immediately when the program is executed.
const MergeSort = () => {
let animations = sortingAlgorithms.mergeSort(array);
console.log(animations.length);
for (let i = 0; i < animations.length; i ) {
// speed ;
// console.log(i);
setTimeout(() => {
// setSpeed(tempSpeed);
const topArrayBars = document.getElementsByClassName("array__bar");
const bottomArrayBars =
document.getElementsByClassName("array__baraux");
if (animations[i].type === "look") {
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = animations[i].look[0]; j <= animations[i].look[1]; j ) {
topArrayBars[j].style.backgroundColor = "rgb(255, 79, 120)";
}
} else if (animations[i].type === "join") {
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
// left color
for (
let j = animations[i].leftIndices[0];
j <= animations[i].leftIndices[1];
j
) {
topArrayBars[j].style.backgroundColor = "rgb(255, 77, 252)";
}
// right color
for (
let j = animations[i].rightIndices[0];
j <= animations[i].rightIndices[1];
j
) {
topArrayBars[j].style.backgroundColor = "rgb(255, 237, 77)";
}
} else if (animations[i].type === "place") {
bottomArrayBars[animations[i].newIdx].style.height =
topArrayBars[animations[i].oldIdx].style.height;
bottomArrayBars[animations[i].newIdx].style.backgroundColor =
topArrayBars[animations[i].oldIdx].style.backgroundColor;
topArrayBars[animations[i].oldIdx].style.backgroundColor =
"rgba(57, 200, 195, 0)";
} else if (animations[i].type === "lift") {
for (
let j = animations[i].range[0];
j <= animations[i].range[1];
j
) {
topArrayBars[j].style.height =
animations[i].newVals[j - animations[i].range[0]].toString()
"px";
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = 0; j < bottomArrayBars.length; j ) {
bottomArrayBars[j].style.backgroundColor = "rgba(57, 200, 195, 0)";
}
}
}, i * getSpeed());
}
the getSpeed function above tries to pull the speed from a useState variable named speed controlled by the slider. However, I cannot figure out how to make the program wait to see what the slider value will be to change the speed of the animation.
I wanted to somehow have the loop iterate as follows: execute once check the speed value, and wait for 1/speed execute again check for speed value and wait for 1/speed ...
CodePudding user response:
The following snippet illustrates the difference between your approach and Bergi's.
var speed = 100;
function action(i) {
document.getElementById("counter").textContent = i;
}
function animation1() {
for (var i = 0; i < 100; i )
setTimeout(action.bind(undefined, i), i * speed);
}
function animation2(steps) {
action(steps);
if (steps < 100)
setTimeout(function() {
animation2(steps 1);
}, speed);
}
<span id="counter"></span>
<input type="range" onchange="speed=this.value" value="100" min="0" max="1000"/>
<button onclick="animation1(0)">Fixed speed</button>
<button onclick="animation2(0)">Flexible speed</button>
CodePudding user response:
I actually figured it out through ChatGPT. The key was that I was setting the timeout on the wrong section in the code. The timeout should be at the end of the loop encompassed in a promise. See code below:
this.setState({ prevArray: this.state.array.slice() });
let animations = sortingAlgorithms.mergeSort(this.state.array);
for (let i = 0; i < animations.length; i ) {
const topArrayBars = document.getElementsByClassName("array__bar");
const bottomArrayBars = document.getElementsByClassName("array__baraux");
if (animations[i].type === "look") {
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = animations[i].look[0]; j <= animations[i].look[1]; j ) {
topArrayBars[j].style.backgroundColor = "rgb(255, 79, 120)";
}
} else if (animations[i].type === "join") {
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
// left color
for (
let j = animations[i].leftIndices[0];
j <= animations[i].leftIndices[1];
j
) {
topArrayBars[j].style.backgroundColor = "rgb(255, 77, 252)";
}
// right color
for (
let j = animations[i].rightIndices[0];
j <= animations[i].rightIndices[1];
j
) {
topArrayBars[j].style.backgroundColor = "rgb(255, 237, 77)";
}
} else if (animations[i].type === "place") {
bottomArrayBars[animations[i].newIdx].style.height =
topArrayBars[animations[i].oldIdx].style.height;
bottomArrayBars[animations[i].newIdx].style.backgroundColor =
topArrayBars[animations[i].oldIdx].style.backgroundColor;
topArrayBars[animations[i].oldIdx].style.backgroundColor =
"rgba(57, 200, 195, 0)";
} else if (animations[i].type === "lift") {
for (let j = animations[i].range[0]; j <= animations[i].range[1]; j ) {
topArrayBars[j].style.height =
animations[i].newVals[j - animations[i].range[0]].toString() "px";
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = 0; j < topArrayBars.length; j ) {
topArrayBars[j].style.backgroundColor = "rgb(57, 200, 195)";
}
for (let j = 0; j < bottomArrayBars.length; j ) {
bottomArrayBars[j].style.backgroundColor = "rgba(57, 200, 195, 0)";
}
}
console.log(i);
await new Promise((resolve) =>
this.timeouts.push(setTimeout(resolve, this.state.delay))
);
}
}
You would then have a slider set the delay, and it works!