Kamel Fakih
Kamel Fakih

Reputation: 47

trying to animate an element to different positions using JavaScript

based on this this tutorial on w3 schools I wrote a function(move) that animates an element(div) using the setInterval() method, my problem is when I try to call the function move() several times the div does an unexpected behavior, I tried to use async/await but it didn't work.

async function moveArray(destination) {
  //move through array of addresses
  for (let i = 0; i < destination.length - 1; i++) {
    await move(destination[i], destination[i + 1]);
  }
}

async function move(pos, add) {
  //moves element from position to address
  var elem = document.getElementById("object");
  var id = setInterval(frame, 15);

  function frame() {
    if (pos.x == add.x && pos.y == add.y) {
      clearInterval(id);
      return;
    } else {
      //if element haven't reached its target
      // we add/substract 1 from the address and update styling
      if (pos.x != add.x) {
        pos.x += add.x > pos.x ? 1 : -1;
        elem.style.left = pos.x + "px";
      }
      if (pos.y != add.y) {
        pos.y += add.y > pos.y ? 1 : -1;
        elem.style.top = pos.y + "px";
      }
    }
  }
}
#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}

#object {
  width: 50px;
  height: 50px;
  position: absolute;
  background: red;
}
<button onclick="moveArray( [{x:0,y:0}, {x:140, y:150}, {x:130, y:110}, {x:70, y:65}] )">
      move
    </button>
<div id="container">
  <div id="object"></div>
  //object to be animated
</div>

Upvotes: 1

Views: 168

Answers (1)

Emiel Zuurbier
Emiel Zuurbier

Reputation: 20944

The weird behavior is caused because the move function is being called multiple times simultaneously in the for loop. This happens because your async functions do not know when they are done.

Instead of making move async, which it doesn't have to if it is not awaiting anything, return a Promise from the start and call you animation code inside of it. When the point is reached when animation has to end, resolve the promise.

Doing it like this will make your await statement in the for loop wait for the move function to reach the resolve call before continuing with the next animation.

async function moveArray(destination) {
  //move through array of addresses
  for (let i = 0; i < destination.length - 1; i++) {
    await move(destination[i], destination[i + 1]);
  }
}

function move(pos, add) {
  return new Promise(resolve => {
    var elem = document.getElementById("object");
    var id = setInterval(frame, 15);

    function frame() {
      if (pos.x == add.x && pos.y == add.y) {
        clearInterval(id);
        
        // Fulfill promise when position is reached.
        resolve();
        
      } else {
        if (pos.x != add.x) {
          pos.x += add.x > pos.x ? 1 : -1;
          elem.style.left = pos.x + "px";
        }
        if (pos.y != add.y) {
          pos.y += add.y > pos.y ? 1 : -1;
          elem.style.top = pos.y + "px";
        }
      }
    }
  });
}
<!DOCTYPE html>
<html>

<head>
  <style>
    #container {
      width: 400px;
      height: 400px;
      position: relative;
      background: yellow;
    }
    
    #object {
      width: 50px;
      height: 50px;
      position: absolute;
      background: red;
    }
  </style>
</head>

<body>
  <button onclick="moveArray( [{x:0,y:0}, {x:140, y:150}, {x:130, y:110}, {x:70, y:65}] )">
      move
    </button>
  <div id="container">
    <div id="object"></div>
    //object to be animated
  </div>
  <script src="animation.js"></script>
</body>

</html>

Upvotes: 1

Related Questions