Success StoriesBlogContact
pile of old painting needing a repaint
Tutorials |

How to avoid unnecessary repaints

Martin Hofmann

20. September 2018

»Hey, is this animation kind of janky?« »Definitely, I’ve been ignoring it successfully for months!« »What’s going on there? Can we make it smoother?« »Um, no clue… Maybe?«

I was working as a front-end developer for one of our customers when I had the above conversation with a member of my team. At that time we were basically asked to analyze the web app and come up with ideas on how to improve UI performance. As a start I did what any web developer would do nowadays: I asked Google. But I only found a few helpful pieces of information on the topic. Some – actually most – of the explanations to our animation problem were rather unspecific and seemed mysterious to me. I quickly realized that this can be caused by a variety of things, including a combination of certain widely-used HTML, CSS and JavaScript patterns which don’t cause issues when used alone but can lead to trouble when combined. One could say that my starting point was not really defined, and as a result I was not able to find a straight-forward solution. I decided to figure out what’s going on in the browser and continue from there.

The first step was simply to use the Paint flashing option in Chrome’s DevTools. So I inspected our suspicious janky animation and found out something curious. Even though the animation should only affect a specific area, it – for whatever reason – caused full (!) page repaints. It took me less than two minutes to find other elements on our site showing similar behavior, like these:

Screencast showing examples of full page repaints when the animation is triggered
Screencast showing examples of full page repaints when the animation is triggered

Examples of full page repaints when the animation is triggered.

So far, so bad. I just wanted to tweak an animation a bit but instead found a wide-reaching issue in our web app. The browser is doing unnecessary work. It is repainting content it is not supposed to. So, what can I do next with my newly discovered knowledge?

In order to find out why the browser behaves like this, I learned a lot about the browser’s rendering pipeline. There are various good resources out there. It was probably the first time I fully understood how a browser generates pixels from code. This diagram shows three different paths the browser can take to render elements to the screen.

Overview of Pixels to screen pipeline according to Google’s Web Fundamentals

After finding out about the third option, the idea of avoiding any layout and painting steps really made sense to me. I checked the code of my animation. Here's a simplified version of it:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Unoptimized
.mobile-menu {
    position: fixed;
    top: 0;
    bottom: 0;
    width: 300px;
    margin-left: -300px;
    transition: all 0.2s ease-out;

    &.open {
        margin-left: 0;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Optimized
.mobile-menu {
    position: fixed;
    top: 0;
    bottom: 0;
    width: 300px;
    transform: translateX(-300px);
    will-change: transform;
    transition: transform 0.2s ease-out;

    &.open {
        transform: none;
    }
}

I started optimizing things one by one.

First, I changed the animated property from margin-left: -300px to transform: translateX(-300px). And this already solved my problem. The animation was not janky anymore. Yeah 🎉 🎉 🎉! I don’t know if the FPS meter would show 60 frames per second now, but you could definitely feel the difference – even on an up-to-date laptop.

So, what’s going on? By using the transform property, we’re allowing the browser to skip the layout and painting step. It just moves around the already painted layers, which is called compositing. This technique is commonly used in cartoons in order to avoid repainting parts of the image that do not change:

Compositing was invented by Walt Disney in the 1950s to speed up the production of cartoons.

Pushing it further

Well, I could have stopped at this point, the animation was pretty smooth. But the paint flashing tool still displayed green rectangles across the whole page. I wanted to push it further.

The next step was to restrict the transition property to only animate the transform property instead of just specifying all. Unfortunately, this didn’t bring any benefit. It was just like before. Nevertheless, I think it is better to restrict it just in case another developer needs to change code here and accidentally animates other properties.

I did some further research and found out about the will-change property. It can improve the performance of CSS animations, but shouldn’t be spread across the whole codebase as it also decreases the performance when used too often. A side navigation is a pretty good use-case, I guess, because we know that it will be animated frequently. Therefore I added it.

Boom! The paint flashing was gone completely. This means that we’re only performing compositing now instead of doing repaints all over the place. The will-change property basically moves the whole element to its own compositing layer so it can be animated without touching other elements. Success! Job done, time to go home 🏠.

Nah, I’m still here for a conclusion, of course…

Conclusion

I learned a lot about the browser and highly recommend the resources linked in the blog post. As a web developer it’s very important to know about the rendering pipeline. A profound knowledge about this topic allows you to find good solutions for a variety of problems without having the feeling of looking for a needle in a haystack. And of course, you can impress your colleagues by explaining all the technical charts within Chrome’s DevTools to them, haha.

By the way

The web site featured in this blog post is SevenCooks, an alternative platform for vegetarian and vegan food. We at Peerigon are trying to make sure every visitor has a rich user experience. You can explore recipes using filters and collect them in your personal profile. Sorry, German language only!

Thanks to Leonhard Melzer and Tanner Hoisington.

Web Development

CSS Animation

Rendering

Browsers

Devtools

Weitere Themen

Irena Reitz, Celestine Auburger, Leonhard Melzer, 18.05.2021

Towards fairness: calculating the Peerigon Gender Pay Gap

Diversity

Salary

Company Culture

Zum Blogartikel

Tanner Hoisington, 20.04.2021

Exploring various voting systems with Konsens

voting systems

ranked choice

positional vote

simple plurality

konsens

Zum Blogartikel

Moritz Jacobs, 12.02.2021

A guide to CSS units — pt. 4: angles, time, dpi and values without units

CSS units

CSS angles

CSS time

unitless

dpi

Zum Blogartikel

Wir sind Peerigon, eine Agentur für Softwareentwicklung.

Peerigon GmbH
Werner-von-Siemens-Straße 6
86159 Augsburg
+49 821 907 80 86 0

mail peerigon

service

Full-stack ConsultingSoftware DevelopmentProgramming WorkshopsTeam Support
BlogSuccess StoriesContactgo digital fundingWhy use TypeScript?

© 2021 Peerigon

Privacy PolicyLegal NoticePress
ClimatePartner