Boost Your CSS Animation Performance with the Repaintless.css Library
CSS animations have been in regular use for a few years now. Used correctly, they are a fantastic way to enhance your website and help users understand interactions better. Unfortunately, as easy as they are to use, there is a high chance that you are forcing your user’s browser to perform costly operations that slow down the whole page. Let’s see: have you ever animated an element’s width, height or top position with CSS? If the answer is “yes”, it means that you triggered expensive layout recalculations that might have resulted in jank when viewed under certain conditions.
Getting to know our friends among animations
The best way to avoid laggy animations is to find ones making good use of our GPU and don’t affect the layout or paint of the website.That is why you should only animate transforms (translate, rotate, scale) and opacity. These properties should easily satisfy your needs when it comes to simple animations. Also, it is best to animate absolutely positioned elements, which won’t push other elements around the page. These two rules are already enough to speed up your framerate to 60fps and set the GPU memory buffer free in most cases. But that’s not all. There is one other handy technique that can help you create really lightweight animations.
The FLIP technique
Last year I had the pleasure to listen to Paul Lewis’ presentation on web performance. It truly blew my mind and buried in amongst few other interesting things there was this gem of awesomeness: the FLIP technique. The simplicity and the advantages of it made me LOVE IT. So what is the FLIP technique? FLIP stands for First, Last, Invert, Play. This quote from Paul’s GitHub repository for the FLIP helper library sums it up perfectly:
FLIP is an approach to animations that remaps animating expensive properties, like width, height, left and top to significantly cheaper changes using transforms. It does this by taking two snapshots, one of the element’s First position (F), another of its Last position (L). It then uses a transform to Invert (I) the element’s changes, such that the element appears to still be in the First position. Lastly it Plays (P) the animation forward by removing the transformations applied in the Invert step.
So basically, you remove transform instead of applying it. Why? Well, this means the browser already knows the points A and B for the element’s journey and is able to start the animation faster. The FLIP technique will give you the best results when an animation is played on user input. The difference might not be huge, but on a phone with a less powerful CPU this can be the difference between it feeling like an immediate or delayed response from the website. Once you get used to the idea, writing animations the FLIP way feels natural. Here’s a small code example using the FLIP technique:
As you can see, I just reversed the order of the animation. Instead of pushing the element 150px from the left to the right, I pulled it to the left with transition’s negative value and then removed that transition entirely (set transform value to “none”).
Building on a new discovery
What I discovered was that not many people seem to know this approach. I couldn’t get it out of my head and decided to do something to convince more and more people to join me on the journey to faster animations. I knew there were many popular animation libraries, eg. animate.css, but they did not use the FLIP method and included animations that might cause website repaints. Therefore, I made a list of moves that can be done using only safe transforms and opacity and decided to build a small CSS library that contains only lightweight animations. Once the animated elements are painted to the browser window (which is really fast btw!), they are running at stable 60 fps and consume next to no browser resources. There are no repaints after that, hence the library name: repaintless.css. The gif below shows the animation running in the browser with the Chrome DevTools FPS meter on:
To show that repaintless.css runs really smoothly, I have prepared a small demo page. As I wrote before, the FLIP technique gives the best results when triggered on user input, so you can start animating elements by clicking “PLAY” on a middle square and see how fast the animation responds. The filters (for now visible only for 768px and wider screens) can help you test different animations individually.
If you are interested in using the library, go to the repaintless.css Github repository and follow the instructions in the readme. If you’d like to help me improve the code or just have an idea for an animation, a pull request is always welcome. Bear in mind that the repository is quite fresh and I am still fine tuning it. In the future, I plan to add more moves and enable custom gulp builds with only the animations you select. At the moment, to achieve that, you need to download the whole repository, remove the unwanted @imports in the repaintless.scss file and run gulp build. Not perfect, but doable. :)
With great power comes great responsibility
I hope that after reading this article, you’ll always think twice before coding animations and try to make them as fast and light as possible. There are plenty of great articles about performance, this one by Paul Andrews and Paul Irish is really worth checking out. Also, there is a terrific page that shows you how animating different attributes affects website load. With this knowledge and a little practice, you’ll become a performance guru in no time.
PS. I wondered how the performance would look like if I built the worst possible version of this animation. I decided to do a quick check with just one element from the demo animation. The result was outrageous! Even with all I’d learned, I didn’t expect so much lag. Shown in the gif below, I animated the margins (never do that!) so it goes from -200px left to -200px right margin (terrible!):