Creating animations in Javascript using requestAnimationFrame
Tags: css, javascript, animation
Published: 2020-07-04 15:43:44

---
title: Creating animations in Javascript using requestAnimationFrame
published: true
date: 2019-05-19 07:37:03 UTC
tags: css,javascript,animation
---

Animations in Javascript? I bet keyframes are better._ I have almost entirely used CSS to create animations for my websites. In fact, I rarely use Javascript directly to touch the styles of an element. But I recently stumbled upon a tutorial using requestAnimationFrame for animations. I expected the usage of it to be complex, but it was very _simple_ yet _powerful_.

### Animations in JavaScript before requestAnimationFrame

Before requestAnimationFrame, it was common to see people use setInterval to update styles of elements so that it was animated similar to the given code below. For a smooth animation we need to match the display framerate, which is 60fps, so we need to execute our function for 60 times per second which translates to an interval of  **17ms**.

```javascript
const progressBar = document.querySelector('#progress-bar')
let width = 0

const updateProgressBar = () => {
  progressBar.style.width = width + '%'
  width += 1
  if (width > 100){
    clearInterval(id)
  }
}

const id = setInterval(updateProgressBar, 17)
```

A function, which updated the style of the element, was executed repeatedly on set intervals. Though this _did the job_, it wasn’t an effective way. There were many disadvantages for this approach. The timer wasn’t accurate i.e the browser would give priority to other UI tasks over this. The function would keep executing even if the user is in some other tab and depending on the execution it meant higher CPU usage.

{% codepen https://codepen.io/digi0ps/pen/xeyXGg %}

### Now what is requestAnimationFrame?

requestAnimationFrame was developed to overcome the shortcomings of the **setInterval/setTimeout** approach providing a native API to run any type of animation. It takes in a function as it’s argument and tells the browser to execute that function before the next repaint.

It is very similar to setInterval except we are requesting the browser to execute the animation at the next available opportunity instead of predefined interval.

Browsers generally optimize execution based on load, battery and element visibility on screen. All the animations are grouped into a single repaint, thus reducing the number of CPU cycles required. The animations are also halted when the user switches to a different tab. Yay!

![](https://cdn-images-1.medium.com/max/480/1*sdoJ3O2cZiPTrrm9WezNmQ.gif)

Now doing the same progress bar with requestAnimationFrame gives us

```javascript
const updateProgressBar = () => {
  progressBar.style.width = width + '%'
  width += 1
  if (width < 100){
    requestAnimationFrame(updateProgressBar) 
  }
}

requestAnimationFrame(updateProgressBar)
```

As we can see, we call the **requestAnimationFrame** initially and then recursively keep calling it until the required _width_ level is reached. Clearly, this syntax is much better than setInterval.

{% codepen https://codepen.io/digi0ps/pen/QPZmGG %}

### Gravity using requestAnimationFrame

Now let’s try to use requestAnimationFrame to implement (a more complex) animation for a Ball experiencing free-fall under gravity.

We will be building a page which creates a ball everytime the user clicks on the page and the ball falls to the bottom. We will be trying to create mimic gravity for the fall.

First we will write a function to create a **ball-like element.** The ball will be div (with _border-radius_ 50%) positioned absolutely. We will pass this function the **x, y** coordinates received from the click event.

```javascript
const createBall = (top, left) => {
  const ball = document.createElement("div");
  ball.classList.add("ball");
  ball.style.top = top + "px";
  ball.style.left = left - 25 + "px";
  document.body.appendChild($ball);
  return ball;
}
```

Here we are creating a **div,** adding the class _ball_ and sets the top/left values before returning the created element.

Next we write a function for initialising the fall animation which creates a function to deal with styles and initialises the animation frame request.

```javascript
const initFalling = ball => {
  const ballHeight = 100
  const acceleration = 9.8 / 60;
  const { innerHeight } = window;

  let fallingSpeed = 0;

  const animateFall = () => {
    const top = parseInt(ball.style.top);
    const newTop = `${top + fallingSpeed}px`;

    // To break the fall, when the ball is near the surface
    if (parseInt(newTop) >= innerHeight - ballHeight) {
      ball.style.top = this.innerHeight - ballHeight + "px";
      ball.style.background = "red";
      return null;
    }

    // Else set the top to the new value
    ball.style.top = newTop;
    fallingSpeed = fallingSpeed + acceleration;
    requestAnimationFrame(animateFall);
  };

  requestAnimationFrame(animateFall);
};
```

Let’s breakdown this function.

Every ball starts with a fallingSpeed of 0 and is accelerated with every execution. Since acceleration due to gravity is 9.8m/s per second and the browser executes our function 60 times each second (general display framerate), so the acceleration per execution is  **9.8/60**.

We write another function inside function and call it **animateFall**. This will be the main function which will be passed to requestAnimationFrame. Pretty straight forward stuff inside. We retrieve the _top_ of the ball and add _falling speed to it._ We check if this **newTop** value is greater the window’s **innerHeight.** If it is not, then we set it to the new value and increment **fallingSpeed** before requesting an animation frame again. If the **newTop** is greater, then the ball has hit the surface so we return from the function.

We are nearly done, all we have to do now is create a event handler and chain these two functions together.

```javascript
const onClickListener = event => {
  const { x, y } = event;
  const ball = createBall(y, x);
  initFalling(ball)
};

document.addEventListener("click", onClickListener, false);
```

The event coordinates and the positioning coordinates are inverted. That is x here is equivalent to the left in positioning and y is equivalent to the top. And _skadoosh_ we have created a _free falling portal._

{% codepen https://codepen.io/digi0ps/pen/eoPQWy %}

### We can still make this better

- **Dying Animation;** added a dying animation based on the impact of the collision. The falling speed is an indicator of how long the ball has travelled in air. So we can add a fading animation in which balls which have travelled more fade faster and those which have travelled less fade slowly.
- **Randomness;** added a random factor for the color and size of the ball.
- **Organize;** orgainize the code into components using _Typescript + Webpack._

### When should I use this instead of keyframes?

Though the performance of both requestAnimationFrame and keyframes/transitions are very close, we can stick to CSS animations for most of the cases.

Animating using JavaScript becomes super useful if the animation depends on some state or data, like the example above where we managed the fall of the ball using JS. We can also use it to create more complex animations like staggering animations or trailing animations (cue: I will be making a tutorial on trailing animations next).

### References

- [Window.requestAnimationFrame()](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
- [requestAnimationFrame - The secret to silky smooth JavaScript animation](http://researchhubs.com/post/computing/javascript/requestAnimationFrame.html)
- [CSS and JavaScript animation performance](https://developer.mozilla.org/en-US/docs/Web/Performance/CSS_JavaScript_animation_performance)

**And…**

![](https://cdn-images-1.medium.com/max/640/1*yNMpWj2Qwis_XVDYcdhoPw.gif)

Thank you for reading!