Why my portfolio stuttered on a trackpad
Scrolling was smooth with a mouse wheel and choppy on a trackpad. The fix was moving every scroll effect from JavaScript into CSS.
- Performance
- CSS
After adding scroll animations to this site, I noticed something odd. With an external mouse, scrolling felt fine. With my MacBook trackpad, it felt stuck, especially when I scrolled slowly.
What was going on
A mouse wheel scrolls in steps. A trackpad sends a steady stream of tiny scroll updates. Every effect on the page, like the hero laptop tilting, project images scaling and words lighting up, was driven by JavaScript that ran on each of those updates and measured where elements were. With a trackpad that is a lot of work on the main thread, and the browser couldn't keep up.
The fix: let the browser do it
CSS now has scroll-driven animations. Instead of listening for scroll events, you tie a normal CSS animation to the scroll position, and the browser runs it off the main thread.
.scroll-scale {
animation: scale-in linear both;
animation-timeline: view();
animation-range: entry 0% cover 45%;
}
@keyframes scale-in {
from { transform: scale(.9); opacity: .55; }
to { transform: none; opacity: 1; }
}I rewrote every scroll effect this way, including the progress bar and the hero tilt, and wrapped them in @supports so older browsers just show the finished state.
The smaller things that helped
- Removed the blur behind the sticky header. A backdrop blur has to be redrawn on every scroll step.
- Added will-change to the animated layers so the browser keeps them ready on the GPU instead of redrawing them at every size.
- Changed a hover effect that animated padding to animate transform instead, so it no longer triggers layout.
One more lesson
Part of the remaining choppiness turned out to be my setup, not the code. My external monitor is a 1080p panel, and at that setting the page moves in whole, fairly large pixels, so slow scrolls look steppy. On the laptop screen it was smooth. It was a good reminder to test on more than one display before blaming the code.