Chrome slow scrolling with fixed position elements
Problem and How to Monitor It
The reason for this is because Chrome for some reasons decides it needs to redecode and resize any images when a fixed panel goes over it. You can see this particularly well with
► Right-Click ➔ Inspect ➔ Timeline ➔ Hit ⏺ Record
► Go back to the page and drag scrollbar up and down (Mouse-wheel scrolling not as effective)
Edit (9/1/2016): Since posting this, Chrome added new features to help monitor this:
► Right-Click ➔ Inspect ➔ Rendering (Bottom tabs)
➔ ☑ Scrolling Performance Issues
➔ ☑ Paint Flashing
➔ ☑ FPS Meter (less important, but can be useful)This will help you identify exactly what elements require repaints on scrolls and highlight them clearly on screen.
This seems to just be a problem with the method Chrome is using to determine if a lower element needs to be repainted.
To make matters worse, you can't even get around the issue by creating a div above a scrollable div to avoid using the position:fixed
attribute. This will actually cause the same effect. Pretty much Chrome says if anything on the page has to be drawn over an image (even in an iframe, div or whatever it might be), repaint that image. So despite what div/frame you are scrolling it, the problem persists.
.
The Easy Hack Solution
But I did find one hack to get around this issue that seems to have few downside.
By adding the following to the fixed elements
/* Edit (9/1/2016): Seems translate3d works better than translatez(0) on some devices */
-webkit-transform: translate3d(0, 0, 0);
Some browsers might require this to prevent flickering
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
This puts the fixed element in its own compositing layer and forces the browser to utilize GPU acceleration.
EDIT: One potential issue was pointed out to me by albb; when using
transform
, all descendantposition:fixed
elements will be fixed to that composition layer rather than the entire page.
.
Alternative Solution
Alternatively, you could simply hide the top navigation while scrolling and bring it back in afterwards. Here is an example that could work on the stackoverflow.com's header or a site like theverge.com if pasted in DevTools > Console (or manually type "javascript:" into this pages URL bar and paste in the code below after it and hit enter):
/* Inject some CSS to fix the header to the top and hide it
* when adding a 'header.hidden' class name. */
var css= document.createElement("style");
css.type = 'text/css';
css.innerHTML = 'header { transition: top .20s !important; }';
css.innerHTML += 'header.hideOnScroll { top: -55px !important; }';
css.innerHTML += 'header { top: 0 !important; position: fixed !important; }';
document.head.appendChild(css);
var header = document.querySelector("header");
var reinsertId = null; /* will be null if header is not hidden */
window.onscroll = function() {
if(!reinsertId) {
/* Hides header on scroll */
header.classList.add("hideOnScroll");
setTimeout(function() { header.style.visibility = "hidden"; }, 250);
} else {
/* Resets the re-insert timeout function */
clearTimeout(reinsertId);
}
/* Re-insert timeout function */
reinsertId = setTimeout(function(){
header.classList.remove("hideOnScroll");
header.style.visibility = "visible";
reinsertId = null;
}, 1500);
};
The first solution of @Corylulu works, but not completely (still a little stutter, but much less).
I also had to add -webkit-backface-visibility: hidden;
to the fixed element to be stutter free.
So for me the following worked like a charm to prevent scroll down stutter in chrome when using fixed elements on the page:
-webkit-transform: translateZ(0);
-webkit-backface-visibility: hidden;
Edit: Webkit-transform and webkit-backface-visibility both cause blurry fonts and images. So make sure you only apply both on the hover state.
Add this rule to your fixed element,
will-change: transform;
Read about solution from Here,
and read about will-change property from Here.