user431806 user431806 - 3 months ago 6
Javascript Question

JS - setInterval while looping for array

I have an array that it filled with objects that hold a attribute that is either called "move" or "turn" and specify how to animate a image on a canvas.
Upon clicking a button, i want to loop over the array and animate the elements in the correct order, i.e. move the object, turn it, move it again.

This is the code:

var animation = false;

this.executePlan = function(){ // part of object
var self = this // part of object

for (var i = 0; i < this.moves.length; i++){
var move = this.moves[i];

if (move.type == "move"){
var path = new Path({x: self.x, y: self.y}, {x: move.x, y: move.y});

animation = setInterval(function(){
console.log("move");
ctx.clearRect(0, 0, res, res);
self.draw();
self.x += path.vector.x;
self.y += path.vector.y;
path.vector.s--;
if (path.vector.s <= 0){
clearInterval(animation);
}
},
10);
}
else if (move.type == "turn"){
animation = setInterval(function(){
console.log("turn");
if (move.a > 0){
self.facing++;
move.a--;
}
else {
self.facing--;
move.a++;
}
ctx.clearRect(0, 0, res, res);
ctx.save();
ctx.translate(self.x, self.y);
ctx.rotate(self.facing*Math.PI/180);
ctx.drawImage(self.img, -self.size/2, -self.size/2, self.size, self.size);
ctx.restore();
if (move.a == 0){
clearInterval(animation);
}
},
30);
}
}
}


For the purpose of this example, i have to animate a straight movement path, than a turn, than a straight movement path and im under the impression that the code should hold, i.e.
first array element triggers if clause, second triggers elseif clause, first triggers if clause.

However, what happens is that the various intervall do overlap, which leads to my image moving out of the canvas, while rotating all the time - instead of moving, then rotating and finally moving straight again

How can i fix the code and what exactly is the error ?

thanks,

Answer

The issue is that you're starting both/all animations at the same time.

You need some mechanism to start subsequent animations only after the earlier animations are done. My suggestion would be to change your for loop into a function, where each animation is responsible for calling the function again (with an incremented i) when its animation is complete.

EDIT

An example of the approach I suggested (completely untested):

var animation = false;

this.executePlan = function() {
    var self = this;

    function doStep(i) {
        var move = this.moves[i];

        if (move.type == "move") {
            var path = new Path({ x: self.x, y: self.y }, { x: move.x, y: move.y });

            animation = setInterval(function() {
                console.log("move");
                ctx.clearRect(0, 0, res, res);
                self.draw();
                self.x += path.vector.x;
                self.y += path.vector.y;
                path.vector.s--;
                if (path.vector.s <= 0) {
                    clearInterval(animation);
                    if (i + 1 < this.moves.length) {
                        doStep(i + 1); // if there's more, do the next step
                    }
                }
            }, 10);
        }
        else if (move.type == "turn") {
            animation = setInterval(function() { 
                console.log("turn");                    
                if (move.a > 0){
                    self.facing++;
                    move.a--;
                }
                else {
                    self.facing--;
                    move.a++;
                }
                ctx.clearRect(0, 0, res, res);
                ctx.save();
                ctx.translate(self.x, self.y);  
                ctx.rotate(self.facing*Math.PI/180);
                ctx.drawImage(self.img, -self.size/2, -self.size/2, self.size, self.size);
                ctx.restore();
                if (move.a == 0) {
                    clearInterval(animation);
                    if (i + 1 < this.moves.length) {
                        doStep(i + 1); // if there's more, do the next step
                    }
                }
            }, 30);
        }
    }

    doStep(0); // start the first animation
}
Comments