Move an object inside a loop that runs 60fps

405 Views Asked by At

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>

3

There are 3 best solutions below

2
On BEST ANSWER

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>

0
On

To have the function runLoop be invoked 60 times per seconds it needs to be invoked with an interval of (1000 / 60) ms.

Also, the counter needs to be initialized.

(You will also need some condition for when to stop the count to avoid overflowing).

<script>
  function runLoop(xcoord) {
      var redBlock = document.getElementById("block");
      redBlock.style.left = xcoord + "px";
  }

  var counter = 0;
  setInterval(runLoop, 16.7, counter++);

</script>
0
On

The parameter of setInterval is wrong, if you want to run it 60 times per second you need to set the interval to 1000 / 60.

Now I guess you are trying to set an animation loop. You probably want to have a look at a function made for that purpose.

requestAnimationFrame(someCallback);

Basically it's a function that will register a callback to be executed before the next browser render call. That means a maximum of 60 times/sec but it can be less if the browser is struggling with computation.

Applied to your example it could be as simple as :

function runLoop() {
      var redBlock = document.getElementById("block");
      redBlock.style.left = xcoord + "px";
      requestAnimationFrame(runLoop()); // register recursivaly a call for next animationFrame
 }
  var counter = 0;

  runLoop()