Low FPS when scrolling a page. Solving the background-attachment: fixed problem
body{
background: url("../images/bg.jpg") no-repeat center center / cover fixed;
}
and pleased pressed F5. Beauty, and more!
I started scrolling down the page and I feel something was wrong ...

It feels like I'm playing Crysis on a very old computer. Why did the brakes start on the site and the scrolling is jerky?
I began my investigation ...
At first I made a mistake on the property
cover, but it was not in it. Turning off the fixed background position (removing fixed), my “Crysis” gave me more than 30 FPS ! “On business ...” I thought. How so? Why? Why haven’t I noticed this before? Perhaps this is not very noticeable on lightweight sites where there are not many html elements. But the thing was this. Using
background-attachment : fixedeach time you scroll, it calls the redraw operation. The page should move its content. And when it comes to a fixed background, the browser must redraw the picture in a new place, relative to the existing DOM elements. To solve this problem, our background image needs its own element so that it can move independently of others. And also we need a CSS3 property
will-change. We will discuss it below. As soon as we solve the problem with drawing, scrolling will no longer take place in jerks. Since the background will lie on its own layer, you no longer need to redraw the page each time you scroll.
Let me show you an example.
This is our original code (I expanded the properties for clarity):
body{
background: url("../images/bg.jpg") no-repeat center center;
background-attachment: fixed;
background-size: cover;
}
And here is what we need to do to solve the problem:
body{
position: relative;
}
body::before {
background: url("../images/bg.jpg") no-repeat center center;
background-size: cover;
content: ' ';
height: 100%;
left: 0;
position: fixed;
top: 0;
width: 100%;
will-change: transform;
z-index: -1;
}
We added
position: relativefor the element bodyto then position the pseudo-element, which will be a separate layer for our background. The rest of the properties regarding the background, we moved to ::before. We now use the pseudo-element position : fixed, instead of the old background-attachment: fixedy body. Well, and the most important thing without which the whole idea crashes is the will-change property . The property
will-changeinstructs the browser to display the element, regardless of other elements surrounding it. It kind of tells the browser: “Hey friend, this element will change sometime later, in the future, so draw it only once on its own layer. And you do not need to take into account the rest of the elements - it is by itself. Such are the things.
I tested this build in different browsers, and here is a short summary:
- Google Chrome Everything is OK, it works like a clock.
- Mozilla Firefox Everything is OK, it works like a clock.
- Opera Everything is OK, it works like a clock.
- Safari Everything is OK, it works like a clock. Thanks for checking smssystem
- Microsoft Edge The method works, but there is one cant. If you turn the wheel, the top and bottom of the page twitches, but then they return to normal. If you twist using a scrollbar, then everything is OK.
- Internet Explorer Same problem as Edge.