-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(#8625): smooth scrolling in SPA mode on iOS #10235
Conversation
🦋 Changeset detectedLatest commit: 4096ff5 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Hi @sanman1k98, thank you for this PR!
What complicates this further is the fact that in most situations we don't need to dynamically record the scroll position: Every time we do a forward navigation to the next page, we synchronously update the last scroll position for the old page and we're done. The whole event-driven recording of scroll positions is only necessary for history navigation, where the history entry for the next page is set before the popstate event is triggered. For this reason, we have no way to record the current scroll position for the page we left in the history state of the browser. A solution that works completely without events could use a data store that is independent of the history state. However, it makes little sense to re-implement the browser's history API for this purpose. I hope that the navigation API can help here. The current solution with events and throttling is not really good: in rare cases we can still lose events for the page we left. The solution proposed in this PR might suffer from the same weakness, as it also might ignore scroll position changes up to 350 ms and a history-back navigation might well fall into this interval. I would be very interested in getting rid of the iOS scrolling issues. Personally, I have been reluctant to "improve" this part of the code without the certainty that it is better (or at least not worse) for all users than before. If we change this, it should be for good. Maybe you can find another solution that directly addresses the iOS issue without degrading the currently working solution for other environments? |
if ( | ||
now - timestamp > 350 | ||
&& y === scrollY && x === scrollX | ||
&& (y !== history.state.scrollY || x !== history.state.scrollX) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if a popstate event took place in the meanwhile, history.state
has changed to the state of the new page ...
} | ||
// Save the current scroll positions. | ||
y = scrollY, x = scrollX; | ||
requestAnimationFrame(cb); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once started on the first page load, this runs forever on each frame?
Even when the browser throttles down animation frames due to inactivity, a new animation frame might well occur 60, 120 or even 240 times a second.
Thank you very much @martrapp for the feedback!
No worries, I’m not gonna give up anytime soon! First, I would like to try to address the points you brought up.
When the throttle time was increased in #8981, it is mentioned that Safari logs a warning when exceed 100 calls to TL;DR calling at a high frequency
Yeah... it does run on every frame... I was tunnel visioned on the fixing the stuttering and thought that I’ll do it properly and add the logic to run the callback only when the user is scrolling, call Hopefully it will be an improvement for all users! |
dead626
to
f487ea0
Compare
Thank you @sanman1k98! I had a first quick look at your new version and this looks in deed like a big improvement over the current throttling. I'm a bit busy right now, but I'm already looking forward to have more time for your PR. |
@sanman1k98 Great! I really appreciate your approach! This is in deed a big improvement and my suggestions will gradually go down to nitpicking ;-)
I'll leave suggestion in the code for those points... Might have overseen something, but that would be my review comments. Again, I'm really happy that you came up with this solution! |
@martrapp Thank you for your suggestions and helping me fix the errors in my code! I will get to implementing those changes later today! |
Suggested changes: - change interval time from 200 to 50ms - initialize `last*` vars together with the call to `setInterval()` - clear interval when scroll positions stop changing, independent of history state Additional changes: - remove unused `throttle()` function - move guarded block to inside `onScrollEnd()` since using history navigation will trigger our "popstate" callback and fire additional "scroll" and "scrollend" events, causing redundant expensive calls to `replaceState()`
I think it's really great what you've done with it! |
* main: [ci] format fix(withastro#8625): smooth scrolling in SPA mode on iOS (withastro#10235) [ci] release (withastro#10292) [ci] format finalize WIP API (withastro#10280) [ci] format fix(markdoc & mdx): Proxy crimes (withastro#10278) [ci] release (withastro#10286) fix(audits): Don't warn about loading on data URIs (withastro#10275) fix(node): Safely create requests (withastro#10285) [ci] release (withastro#10265) [ci] format fix(assets): Solidify Node endpoint (withastro#10284)
This fix throws a |
Hi @glib-0, |
Changes
Fixes #8625.
Instead of adding an event listener for hundreds of
scroll
events (since Safari doesn't supportscrollend
events), I usedrequestAnimationFrame()
to check if the window stopped scrolling and update accordingly.I'm not experiencing stuttering with this change but I'm not sure if it's the "correct" way to do it. Thoughts?
Testing
Built a separate project with the
<ViewTransitions />
router and previewed on an iPad Pro 11" M1 (ProMotion 120Hz) and 2015 MacBook Pro 13" (60Hz).Placed an iPhone 15 Pro on a flat surface and watched inertial scrolling 6 inches away from my face for 45 seconds.
Docs
N/A just a bug fix.