fix(paginator): re-apply the settled scroll offset when WebKit clamps it - #73
Merged
Conversation
cssAnimateScroll turns a page by translating every view element, then in its cleanup clears the transforms and assigns the final scroll offset. Translating the children shrinks the container's scrollable overflow by the animated distance, and WebKit on iOS 18 keeps clamping against that shrunken extent for the rest of the task, so the assignment lands short. Mid-book the clamp sits far beyond the target and is invisible. On the last page of the book, where the target is exactly the maximum scroll offset, it lands a full page short and the page visibly snaps back, so the final page of long books cannot be reached at all. Forcing a layout first is not enough: the extent it reports is restored, but the scroll clamp still uses the stale one. Detect the short landing and re-apply on the next frame, which is past the compositor's animation teardown. Verified on an iPhone XR / iOS 18.5 simulator with the EPUB from readest/readest#5663: reading straight through now reaches 277/277 instead of sticking at 276/277. Not reproducible on iOS 26.3. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cssAnimateScrollturns a page by puttingtransform: translateX(-delta)on every view element, then in its cleanup clears the transforms and immediately assigns the final scroll offset.Translating the children shrinks the container's scrollable overflow by the animated distance, and WebKit on iOS 18 keeps clamping against that shrunken extent for the rest of the task, so the assignment lands short.
Mid-book the clamp sits far beyond the target and is invisible. On the last page of the book, where the target is exactly the maximum scroll offset, it lands a full page short and the page visibly snaps back, so the final page of long books cannot be reached at all.
Device trace from the failing turn (iPhone XR / iOS 18.5):
maxdrops by exactly one page (414px) between issuing the turn and applying it, while the summed view widths (rvs) are unchanged.Forcing a layout first is not enough: the extent it reports is restored, but the scroll clamp still uses the stale one for the rest of the task, measured on device:
So the fix detects the short landing and re-applies on the next frame, which is past the compositor's animation teardown.
Verification
Verified on an iPhone XR / iOS 18.5 simulator with the EPUB from readest/readest#5663, reading straight through the book:
3/4, 276/277)Exactly one clamp event occurs in a 309-turn run, at the last page. Not reproducible on iOS 26.3, and not on other engines: every non-iOS platform takes the
rafAnimateScrollpath for a book that size, which writes the scroll offset directly each frame and never uses transforms.