Animate/shim webpage scroll with requestAnimationFrame
Tags: javascript, animation, tricks
Published: 2019-07-14 17:00:56

---
title: Animate/shim webpage scroll with requestAnimationFrame
published: true
description: 
tags: javascript, animation, tricks
---

![](https://thepracticaldev.s3.amazonaws.com/i/8n1euynk65hupaqtfkop.gif)

Some sites that I found amazing inside [Awwwards](http://awwwards.com/), 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.


```js

// 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);
        };
}());
```

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

#The basic HTML structure
```html
<html>
  <!-- ...  -->
  <body>
    <!-- #app will be used for wrapper of your website content -->
    <div id="app">
      <!-- content will be here -->
    </div>
  </body>
  <!-- ...  -->
</html>
```

and the Javascript

```js
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
});
```

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`.

```js
let fakeDiv = document.createElement('div');
fakeDiv.style.height = wrap.clientHeight + 'px';
document.body.appendChild(fakeDiv);
```

##Updating the scroll progress

```js
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();
```

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


That's it. [Demo can be accessed here](https://ampersanda-demo-rafscroll.netlify.com/).


And here's the [complete code](https://github.com/ampersanda/shimming-page-example/blob/master/index.html).

Thank you for taking time to read this article.

Happy coding 😊