Skip to content

Instantly share code, notes, and snippets.

@andrit
Created July 10, 2018 16:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrit/5988283fa534d38aa4e6bb8406ab554c to your computer and use it in GitHub Desktop.
Save andrit/5988283fa534d38aa4e6bb8406ab554c to your computer and use it in GitHub Desktop.

//animate DOM elemnst using js //setInterval //setInterval function calls a callback function every n milliseconds and we have to pass that n milliseconds as second argument after callback function.

//If you need to achieve 60 FPS, that means you need to move an element 60 times in a second. //That means, element has to move in roughly about (1000 ms /60 frames) = 16.7 ms

//script

var element = document.getElementById('box');
var left = 0;

var animateCallback = function() {
	element.style.marginLeft = (++left) + 'px';

  // clear interval after 60 frame is moved
  if (left == 60) {
    clearInterval(interval);
  }
}

var interval = setInterval(animateCallback, (1000 / 60));

//markup
<div id="box"></div>

//style
#box {
  width: 50px;
  height: 50px;
  background-color: #000;
}

//In nutshell, setInterval is a Web API that defers execution of some callback function. //But a callback function is always blocking. //Which means if our web page is busy doing something else, this callback has to wait until stack is empty.

/*

  • Have you heard term frame loss? A browser can refresh it’s screen at maximum some m number of times per second. That number m depends on screen refresh rate of your
  • computer, specified refresh rate of your browser and how fast your CPU or GPU is. If your browser has capability to refresh screen at only 30 FPS (due to one or more above reasons), then there is no point of running animations at 60 FPS. Your extra frame will be lost. Meanwhile, you are making changes to the DOM structure (layout) more times than your browser can render which also known as layout thrashing because these operations are synchronous and hampers your website performance as well as paint operations resulting into poor animations.

Hence you need some sort of callback function from browser that tells you when next screen refresh or more accurately when next paint operation will be performed. This is nothing but your requestAnimationFrame Web API.

As now you know that rAF is a Web API, that means, callback will be called asynchronously. Unlike setInterval, requestAnimationFrame does not accept delay argument, instead it only calls the callback function when browser is ready to perform next paint operation */

/*You might have noticed some differences between setInterval and requestAnimationFrame. First, requestAnimationFrame does not get called automatically at every paint. You need to make a request every time when you make changes to the element. These calls will be stacked and executed one by one when next pain operation is scheduled by the browser. These stacking can be done in for loop, while loop or more accurately in recursive function.

requestAnimationFrame returns an id of the request which is an integer. You can use that id to cancel rAF request using cancelAnimationFrame(id) which will throw away the execution of callback, thus stopping the animation (because we are using recursion here).

rAF does not guarantee to give 60FPS animation, it is just a way to avoid frame loss and increase efficiency which will help you achieve more FPS. That means, if you are using how much pixels has moved to cancel the animation, for example 60px in above case, then depending on refresh rate of your browser on your system, the animation can last for 1 second (60 FPS) or 2 seconds (30 FPS) or more. */

//When you pass callback function to requestAnimationFrame, rAF passes timestamp argument which is time elapse in milliseconds since web page was loaded. //timestamp gives exact time when the callback is called.

var element = document.getElementById('box');
var startTime;
var duration = 1000; // 1 second or 1000ms
var distance = 60; // 60FPS

var rAFCallback = function( timestamp ){
	startTime = startTime || timestamp; // set startTime is null
  
  var timeElapsedSinceStart = timestamp - startTime;
  var progress = timeElapsedSinceStart / duration;
  
  var safeProgress = Math.min( progress.toFixed(2), 1 ); // 2 decimal points
  
  var newPosition = safeProgress * distance;
  
  element.style.transform = 'translateX('+ newPosition + 'px)';
  
  // we need to progress to reach 100%
  if( safeProgress != 1 ){
  	requestAnimationFrame( rAFCallback );
  }
}

//If you are interested in RxJS, you can use animationFrameScheduler to create animations. //https://www.youtube.com/watch?time_continue=1844&v=X_RnO7KSR-4 Changed in RxJS 5...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment