fix: reduce scroll animation jank on Android#37
Conversation
Android Chrome batches/defers IntersectionObserver callbacks during momentum scrolling and blocks scroll on non-passive listeners, causing scroll-triggered animations to appear choppy and load late compared to iOS Safari. - enterViewport: add a bottom rootMargin lead time so reveals fire before the throttled callbacks lag, and guard against a zero/not-yet laid-out element height that could make the threshold NaN and throw. - pageScrolling/elementScrolling: use passive scroll listeners and coalesce work into a single requestAnimationFrame per frame instead of running anime.js on every scroll event. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughScrolling interactions now coalesce animation seeks per animation frame with passive listeners and cleanup cancellation. Viewport-entry interactions guard threshold calculation against invalid dimensions and trigger observation earlier using a bottom root margin. ChangesFrontend interaction updates
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 Pull request artifacts
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/interaction-types/frontend/pageScrolling.js (1)
17-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the shared rAF coalescing pattern.
The
scrollHandler+ rAF coalescing + passive listener + cleanup logic is now duplicated verbatim betweenelementScrolling.jsandpageScrolling.js. A shared helper (e.g.,createRafThrottledScrollListener(update)) would eliminate the duplication and ensure both files stay in sync — the missing clamp above is a direct consequence of this divergence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/interaction-types/frontend/pageScrolling.js` around lines 17 - 42, Extract the duplicated scroll-event rAF coalescing, passive listener registration, and cancellation cleanup from pageScrolling.js and elementScrolling.js into a shared helper such as createRafThrottledScrollListener(update). Update both callers to use the helper while preserving immediate update behavior, animation destruction, and listener cleanup, ensuring shared logic remains synchronized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/interaction-types/frontend/pageScrolling.js`:
- Around line 10-15: Clamp the calculated scrolled value to the [0, 1] range
before passing it to animation.seekPercentage in the update function, matching
the behavior of elementScrolling.js and preserving the existing first-call
smoothness handling.
---
Nitpick comments:
In `@src/interaction-types/frontend/pageScrolling.js`:
- Around line 17-42: Extract the duplicated scroll-event rAF coalescing, passive
listener registration, and cancellation cleanup from pageScrolling.js and
elementScrolling.js into a shared helper such as
createRafThrottledScrollListener(update). Update both callers to use the helper
while preserving immediate update behavior, animation destruction, and listener
cleanup, ensuring shared logic remains synchronized.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d6506bc6-82b9-4083-8454-0d910451a705
📒 Files selected for processing (3)
src/interaction-types/frontend/elementScrolling.jssrc/interaction-types/frontend/enterViewport.jssrc/interaction-types/frontend/pageScrolling.js
Clamp page-scrolling progress to [0, 1] to match elementScrolling and avoid invalid seeks during iOS overscroll. Apply the enter-viewport rootMargin lead time only on Android so desktop and iOS keep the previous intersection behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Summary
Fixes choppy / late-loading scroll-triggered animations on Android Chrome (reported for the WPI website — animations that appear promptly on iOS Safari "pop in" late or stutter on Android). Ref: gambitph/WP-Interactions#13.
Root causes and fixes:
enterViewport— Android Chrome batches/defersIntersectionObservercallbacks during momentum (fling) scrolling, so reveals fire noticeably late vs iOS Safari. Added a bottomrootMarginlead time (0px 0px 15% 0px) so the observer triggers slightly before the element scrolls in. Also guarded against a zero / not-yet-laid-out element height, which previously made the normalized thresholdNaNand could throw when constructing the observer.pageScrolling/elementScrolling— scroll listeners were non-passive (blocking scroll on mobile) and ran anime.js on every single scroll event. Switched to{ passive: true }and coalesced work into a singlerequestAnimationFrameper frame. Also guarded divide-by-zero when the scroll range is 0.Test plan
Made with Cursor
Summary by CodeRabbit