DEV Community

Ampersanda
Ampersanda

Posted on • Edited on

Animate/shim webpage scroll with requestAnimationFrame

Some sites that I found amazing inside Awwwards, are using this method from shimming the scroll of their webpage.

requestAnimationFrame is special function for your animation to work, basically you use loop to make changes every few milliseconds. So it’s basic API for use with animation, whether that be DOM-based styling changes, canvas or WebGL.

Supporting requestAnimationFrame

As we know, browsers are different, so we have to set some fallback for requestAnimationFrame.


// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel

(function() {
    var lastTime = 0;
    // checking website vendors that has their own requestAnimationFrame
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());
Enter fullscreen mode Exit fullscreen mode

Code above is more like checking and give the robust solution of requestAnimationFrame and cancelAnimationFrame fallback.

The basic HTML structure

<html>
  <!-- ...  -->
  <body>
    <!-- #app will be used for wrapper of your website content -->
    <div id="app">
      <!-- content will be here -->
    </div>
  </body>
  <!-- ...  -->
</html>
Enter fullscreen mode Exit fullscreen mode

and the Javascript

document.addEventListener('DOMContentLoaded', function() {
    // get the #app
    let wrap = document.getElementById('app');

    // set the styles of #app
    wrap.style.position = 'fixed';
    wrap.style.width = '100%';
    wrap.style.top = '0';
    wrap.style.left = '0';

    // initialize #app position to the window
    // on top of page
    wrap.style.transform = 'translateY(0)'; // you can also use top
});
Enter fullscreen mode Exit fullscreen mode

From above code, we fixed the position of #app div, it's because actually we will simulate the scroll animation using CSS transform: translateY() or top animation;

Getting the scroll progress

Our #app has fixed position which means the body doesn’t care about its height anymore, this will deactivate scroll event and remove scrollbar from browser.

So we have to create an empty div which has the height of the #app.

let fakeDiv = document.createElement('div');
fakeDiv.style.height = wrap.clientHeight + 'px';
document.body.appendChild(fakeDiv);
Enter fullscreen mode Exit fullscreen mode

Updating the scroll progress

let update = function () {
  window.requestAnimationFrame(update);

  if (Math.abs(scrollTop - tweened) > 0) {
    // you can change `.072` for the acceleration of scroll
    let top = tweened += .072 * (scrollTop - tweened), // update value of Y translation 
        wt = wrap.style.transform = `translateY(${(top * -1)}px)`;
  }
};

// optional function for adding event
let listen = function (el, on, fn) {
    (el.addEventListener || (on = 'on' + on) && el.attachEvent)(on, fn, false);
};

let scroll = function () {
  scrollTop = Math.max(0, document.documentElement.scrollTop || window.pageYOffset || 0);
 };

listen(window, 'scroll', scroll);

// trigger the update function
update();
Enter fullscreen mode Exit fullscreen mode

Note: you need to update the height of the fake scroll div when resize the page

That's it. Demo can be accessed here.

And here's the complete code.

Thank you for taking time to read this article.

Happy coding 😊

Top comments (0)