diff --git a/frontend/src/assets/pagerite.css b/frontend/src/assets/pagerite.css index 7c4de14..e745945 100644 --- a/frontend/src/assets/pagerite.css +++ b/frontend/src/assets/pagerite.css @@ -73,6 +73,10 @@ a { html { scroll-behavior: smooth; + /* No rubber-band bounce past the page ends (macOS trackpads): the banner + parallax in --pry is driven by scrollY and overscroll would let the + artwork drift beyond its designed range. */ + overscroll-behavior: none; /* Native-scrollbar fallback styling (JS off or before pagerite.js runs): thin, theme-muted thumb on a transparent track. With JS the scrollbars are replaced by OverlayScrollbars (see pagerite.js) — floating, diff --git a/frontend/src/pagerite.js b/frontend/src/pagerite.js index 70c532c..c57c3ec 100644 --- a/frontend/src/pagerite.js +++ b/frontend/src/pagerite.js @@ -401,16 +401,25 @@ import "overlayscrollbars/overlayscrollbars.css"; // themes for their own effects (e.g. the purple sun rising faster than // the drift). Event-driven only: perfectly still when the page is idle. if (!reduceMotion.matches) { - let ticking = false; + // rAF loop that eases the value toward the live scroll position. Reading + // scrollY every frame (rather than only on scroll events) also picks up + // the in-between positions of Chrome/macOS momentum scrolling, whose + // scroll events fire late and coarsely. The loop idles once settled. + let value = Math.min(scrollY * 0.1, 30); + let running = false; const drift = () => { - ticking = false; - document.documentElement.style.setProperty( - "--pry", `${Math.min(scrollY * 0.1, 30)}px`, - ); + const target = Math.min(scrollY * 0.1, 30); + value += (target - value) * 0.12; + if (Math.abs(target - value) < 0.05) { + value = target; + running = false; + } + document.documentElement.style.setProperty("--pry", `${value}px`); + if (running) requestAnimationFrame(drift); }; addEventListener("scroll", () => { - if (!ticking) { - ticking = true; + if (!running) { + running = true; requestAnimationFrame(drift); } }, { passive: true });