Chapter 365: ScrollTrigger.normalizeScroll
Core Idea
Forces scrolling to be done on the JavaScript thread, ensuring screen updates are synchronized and the address bar doesn't show/hide on [most] mobile devices.
Key Concepts
-
normalize: Boolean | Object
- Address bar showing/hiding on mobile browsers, resizing the viewport - have you ever noticed a sudden shift after the address bar shows/hides? That's because the when the viewport resizes, ScrollTrigger must recalculate the start/end positions and they likely change at the new viewport size (hence the jump). It's logically impossible to keep things accurate in terms of trigger positions AND avoid any changes. This isn't a bug in ScrollTrigger. You could prevent it from recalculating (see config()) but then your trigger positions become inaccurate.
- Multi-threaded synchronization issues - if you scroll quickly you may see a pinned ScrollTrigger appear to jump when it initially pins/unpins. Why? Because modern browsers handle scrolling on a different thread, so it may repaint the screen as if the page was scrolled past the point of the pinning...and then the JavaScript thread runs a few milliseconds later and applies the pinning, causing the perceived jump. See Firefox's explanation.
- iOS Safari bugs which misreport position data, causing jitter - some of these bugs have been around since 2017 and still haven't been fixed. The browser misreports scroll position as well as event.clientX/Y intermittently, causing things to "jitter". So when ScrollTrigger asks the browser "what's your scroll position" or "where is the user's finger on the screen?", iOS Safari provides the wrong value quite frequently.
- Overscroll behavior - some browsers like iOS Safari ignore the
overscroll-behavior CSS and force the annoying overscroll bouncing behavior when you reach the top or bottom of the page.
- Inconsistent momentum scroll across devices - Android and iOS touch-scroll with momentum very differently.
- Prevents the address bar from showing/hiding on [most] mobile devices, maintaining a consistent viewport size (resize shifts disappear). One exception we know of is the most recent version of iOS, only on phones in portrait orientation where the browser forces the show/hide (it seems impossible to work around, but you can still use
ScrollTrigger.config({ ignoreMobileResize: true}) to skip refreshes in that case).
- Prevents over-scroll and bounce-back scroll behavior.
Code Examples
ScrollTrigger.normalizeScroll(true); // enable
ScrollTrigger.normalizeScroll(false); // disable
let normalizer = ScrollTrigger.normalizeScroll(); // gets the Observer instance that's handling normalization (if enabled, of course)
- What it demonstrates: typical usage of ScrollTrigger.normalizeScroll in a GSAP animation.
Key Takeaways
-
normalize: Boolean | Object
- Address bar showing/hiding on mobile browsers, resizing the viewport - have you ever noticed a sudden shift after the address bar…
- Multi-threaded synchronization issues - if you scroll quickly you may see a pinned ScrollTrigger appear to jump when it initially…
- iOS Safari bugs which misreport position data, causing jitter - some of these bugs have been around since 2017 and still haven't been…
- Overscroll behavior - some browsers like iOS Safari ignore the
overscroll-behavior CSS and force the annoying overscroll bouncing…
Connects To