Shniper Shniper - 7 months ago 13
Javascript Question

Preventing Multiple Animations from occurring at the same time in loop

I'm making a simple Simon Says style game and use a for loop to go through the array of colors that have been added. Currently every time you click the button it will add a color and run the for loop. My problem is that when two conditionals are met, instead of animating one color then the next, it animates both at the same time. I tried using setInterval which just made an infinite loop, and setTimeout just makes the first animation wait before it happens. Does anyone know how I can get each color to animate one at a time?

In my demo please scroll to the bottom of the js. The top is color.js which allows the color animation. Click the button a few times to see how the animations are behaving.

DEMO

var colors = [];
var red, yellow, blue, green;

//random number
mathF = function() {
return Math.random();
};

//determine what color is added
function newColorF() {
var math = mathF();
if(math <= .25) {
newColor = 'red';
}
else if(math <= .5) {
newColor = 'green';
}
else if(math <= .75) {
newColor = 'yellow';
}
else {
newColor = 'blue';
}
return newColor;
}

function computerTurn() {
newColor = newColorF();
colors.push(newColor);
var colorlength = colors.length;
setTimeout(function() {
for(i=0; i<colorlength; i++) {
x = colors[i];
console.log(x);
if(x === 'red') {
$('#red').animate({
backgroundColor: '#ff9e9e'
}, 750)
.delay(250)
.animate({
backgroundColor: 'red'
}, 750);
}
else if(x === 'green') {
$('#green').animate({
backgroundColor: '#bdffbd'
}, 750)
.delay(250)
.animate({
backgroundColor: 'green'
}, 750);
}
else if(x === 'yellow') {
$('#yellow').animate({
backgroundColor: '#ffffc2'
}, 750)
.delay(250)
.animate({
backgroundColor: 'yellow'
}, 750);
}
else {
$('#blue').animate({
backgroundColor: '#a3a3ff'
}, 750)
.delay(250)
.animate({
backgroundColor: 'blue'
}, 750);
}
}
}, 500);
}
//when begin button is pushed
function beginGame() {
computerTurn();
}

$('#startGame').click(function() {
beginGame();
});

Answer

One possible solution to this that I can think of is to use the complete callback parameter of the animate function.

function computerTurn() {
    newColor = newColorF();
    colors.push(newColor);
    var colorlength = colors.length;

    var i = 0; // Our iteration variable

    var check = function() { // the function to increment i and go to next step of animation, or exit
        i++;
        if (i < colorlength) {
            step();
        }
    };

    var step = function() {
        x = colors[i];
        console.log(x);
        if (x === 'red') {
            $('#red').animate({
                    backgroundColor: '#ff9e9e'
                }, 750)
                .delay(250)
                .animate({
                    backgroundColor: 'red'
                }, 750, check); // <--- attached check function here
        } else if (x === 'green') {
            $('#green').animate({
                    backgroundColor: '#bdffbd'
                }, 750)
                .delay(250)
                .animate({
                    backgroundColor: 'green'
                }, 750, check); // <--- attached check function here
        } else if (x === 'yellow') {
            $('#yellow').animate({
                    backgroundColor: '#ffffc2'
                }, 750)
                .delay(250)
                .animate({
                    backgroundColor: 'yellow'
                }, 750, check); // <--- attached check function here
        } else {
            $('#blue').animate({
                    backgroundColor: '#a3a3ff'
                }, 750)
                .delay(250)
                .animate({
                    backgroundColor: 'blue'
                }, 750, check); // <--- attached check function here
        }
    };

    step(); // For the initial step in the animation
}

Here, what I've done is instead of using a for loop to iterate through the colors array, I have used a recursive approach via the complete callback which animate accepts as its last parameter.


From the documentation on complete:

complete

Type: Function()

A function that is called once the animation on an element is complete.


I've broken the code into 2 functions: step and check.

step executes the next step of the animation.

check is used to check if the array is fully iterated. If it is, then exit. Otherwise, call step.

At the very end, I have called step manually to begin the animation sequence.


NOTE: There are probably better solutions out there, I am not sure.