Search code examples
javascriptrunloop

Move an object inside a loop that runs 60fps


I'm trying to setup a run loop, that will execute 60 times per second -- in my example I'd like to simply move add a px to the left position of a div every time the loop runs, but I think I'm doing something incorrectly.

Would appreciate any help on manipulating this block each time the loop runs.

function runLoop() {
    var counter = counter + 1;
    var redBlock = document.getElementById("block");
    redBlock.style.left = counter + "px";
}

setInterval(function () {
    runLoop();
}, 60)
#block {
  background-color: red;
  width: 100px;
  height: 100px;
  position: absolute;
  display: block;
}
<div id="block"></block>


Solution

  • You should never use setInterval/setTimeout for animation because the delay X that you set is actually "when X have passed". It could also happen that the animation occurs between frame updates of the screen, which makes the animation look janky.

    var counter = 0,
        running = true;
    
    function runLoop() {
        counter = counter + 1;
        var redBlock = document.getElementById("block");
        redBlock.style.left = counter + "px";
    }
    
    setInterval(function () {
        runLoop();
    }, 60)
    #block {
      background-color: red;
      width: 100px;
      height: 100px;
      position: absolute;
      display: block;
    }
    <div id="block"></block>

    What I would recommend is to use requestAnimationFrame which do the calculation and then waits for the next screen update to draw the new position. It could look tricky, but it's easier than it seems, as soon as you realize that it's just a callback to itself.

    I added two buttons so you can play around with the animation.

    Notice how smooth the animation is in comparison to setInterval.

    In addition, do variable declarations outside of loops to improve performance.

    let counter = 0,
        isRunning = true;
    
    const redBlock = document.getElementById("block");
    
    function runLoop() {
      counter = counter + 1;
      redBlock.style.left = counter + "px";
      
      if (isRunning) {
        requestAnimationFrame(runLoop);    
      }
    }
    
    function restart() {
      counter = 0;
      
      if (!isRunning) { runLoop(); } 
    }
    
    function pause() {
      isRunning = !isRunning;
      
      if (isRunning) { runLoop(); } 
    }
    
    requestAnimationFrame(runLoop);
    #block {
      background-color: red;
      width: 100px;
      height: 100px;
      position: absolute;
      top: 30px;
      display: block;
    }
    <div id="block"></div>
    
    <button onclick="pause()">Pause</button>
    
    <button onclick="restart()">Restart</button>