How to develop fast web animations by using FLIP and RAIL techniques

How to get your animations to render fast as lightning

7 min read
by Emiel Muis | Fri Sep 17 2021

Renders

Let's say a <div> element needs to be rendered on a web page. Then the browser first looks at what that element should look like. This is the '(re)-calculate' phase. Here we look at the CSS that gets the <div>. Then we look at the 'bigger picture'. This happens in the 'Layout' phase. It looks at where this <div> should be placed on the page. When that planning phase ('Recalculate style' and 'layout') is over, the <div> can be drawn ('paint' phase) on the page.

Visual representation of render methods

Since we are talking about performance within web animation and renders, we want to make sure that the browser has to do as little as possible. We will try to achieve that by not animating css properties such as width, height, top, left and the like. If you try to animate these properties, all three rendering phases will be started again. If we use css properties like 'Transform: translate' instead, the browser only needs to calculate where the element should go.

The phases printed in yellow (in the example above) are phases calculated by your GPU (graphics card). 'Paint', the blue printed phase, is calculated by your CPU. It is a lot slower in making calculations. So you want to use the CPU as little as possible. You can take care of that by using transforms.

In other words

You have to see it this way. Suppose that I want to make a drawing of a figure. Then, I first think about how big that figure should be ('(re)-calculate style'), where that figure should be on the drawing ('layout'). Finally, I have to actually draw the figure (‘paint’). If I then want that figure to appear on the right side of my drawing, I will have to erase it first and start drawing again.

This is what also happens when animating width, height, top and left. But, I can save myself (the CPU) a lot of hassle, if I cutout and move the figure. Then I no longer have to look at where it needs to go, because it is shifted relatively from its previous position and I no longer have to erase it and draw it again. All I have to do is move the figure.

That's how it works on the web. You save the browser a lot of computing power by shifting something via, for example, transform: translate3d(x, y, z). If you make something shift by adjusting the margins, left, height or width, it has to recalculate the layout with every frame and 'paint' the page again.

Layers

We just talked about a cutout figure. This figure now has its own layer above the rest of the paper. A nice word for this is: "layer promotion". But how do you give an HTML element its own layer?

You can do this, for example, with transform: translate3d(x, y, z). The browser then thinks “oooh 3d, this can get complicated, I'll give it its own layer”. This is super beneficial, because again this means you only load your CPU once, instead of every frame in your animation.

Best practices of animating on the web

Tips and Tricks

If you have placed an element on a web page. Do you know exactly how far this element is from the left side of the screen? Don't worry, I don't know this either. .getBoundingClientRect() knows this. By selecting your element, in this case <div id=“foo”>, with the querySelector, you can get data like 'left, top, width etc' from this element by using .getBoundingClientRect. Your code will then look like this:

const fooCoords = document.getElementById(‘foo’).getBoundingClientRect()

The constant 'fooCoords' now contains an object with all dimensions and coordinates of your <div>.

Use case

I can imagine you are now wondering, “I thought this talk was about web animation. What does .getBoundingClientRect() have to do with this?”.

Let's say you have an e-commerce website, and you want that when someone clicks on your product, the product flies into the shopping cart. Then you first calculate the current position of the image of the product you click on and then the current position of the shopping cart. Since you now know (thanks to .getBoundingClientRect()) the coordinates of both elements, you can calculate how many pixels the photo of the clicked product has to move to get to the shopping cart. We call this difference 'deltaX' and 'deltaY'. If we give these values to a transform, your element moves to the shopping cart. Just don't forget to give that image a transition: transform.

screenshot on how to write the code in which you calculate the different coordinates

FLIP

Flip is a pattern created by Paul Lewis. A scholar when it comes to performance, web animation and the combination thereof.

FLIP stands for First, Last, Invert, Play. But what does this mean?

The FLIP method works as follows.

  1. You collect the coordinates of an object (in the example below for example from the photo of the Potato (<img class=“photo” src=“potato.png”/>)). You store this in a constant.
  2. You give that image a class, for example '.fullSize'. For example, this class contains width: 100vw; height: 100vh;. Then you also store those coordinates, but in a different constant.
  3. You remove the class as soon as possible. We no longer need these and will no longer use them.

The steps on how to follow the FLIP-pattern

Just for clarification. These steps all happen while rendering the page. Since this can be calculated so quickly, it happens even before the paint. Because of this you will not see a flash of a, in this case, gigantic potato.

  1. So now you have all the coordinates of the two states of your element. So we have had the F, L and I steps of the FLIP roadmap. Now the Play part. With the coordinates you can now, for example, create keyframes in JavaScript with the Web Animation API, also called WAAPI. This is not an external library, but is just built into JavaScript. Below is a picture of how to build keyframes in JavaScript using the Web Animation API.

How to use the Web Animation API

We call the function animate() on photo. animate() has two parameters. First an Array with keyframes and then an object with settings. Since we only have two keyframes, you can think of it as a from-to animation. In the object you indicate how long the animation should last, you optionally give easing, fill and direction and then you have made your FLIP animation. Now you just have to make sure that your animation is called in response to an 'event' and you're done.

Creating a toggle function

The RAIL principle combined with FLIP

According to the RAIL principle (Response, animation, idle, load), your response should not last longer than 100ms. Normally, the response of a click event takes about 10 - 30 milliseconds, so FLIP still has about 70ms to do its calculations and generate keyframes. We do this to make the A of RAIL, Animation, run as smoothly as possible. According to the RAIL principle, you have to play animation at 16ms per frame. Better known as 60 frames per second.

RAIL in the real world, a youtube video of Paul Lewis explaining the RAIL principle

Free tip for home

Finally, I would like to inform you that not everyone likes flying potatoes in their screen. Some people can't handle animations or moving images themselves. There are people who experience motion sickness when they visit pages with movement in them. To keep the web as accessible as possible for everyone, it is important to take this into account. So always make sure to apply the media feature @media(prefers-reduced-motion) to animation elements. This can be done very easily and only takes a few seconds of work.

Prefers reduced motion code

by @

All rights reserved