Implement native inverted behaviors for ScrollView
This PR modifies ScrollViewManager and VirtualizedList to work around the limitations of the RN core approach to list inversion. Specifically, the RN core approach to list inversion uses scroll axis transforms to flip the list content. While this creates the visual appearance of an inverted list, scrolling the list via keyboard or mouse wheel results in inverted scroll direction.
To accomplish native inverted scroll behaviors, we focused on four expected behaviors of inverted lists:
- When content loads "above" the view port in an inverted list, the visual content must remain anchored (as if the content renders below it in a non-inverted list).
- When scrolled to the "start" of an inverted list (in absolute terms, the bottom of the scroll view), content appended to the start of the list must scroll into view synchronously (i.e., no delay between content rendering and view port changing).
- When content renders on screen, it must render from bottom to top, so render passes that take multiple frames do not appear to scroll to the start of the list.
- When imperatively scrolling to the "start" of the content, we must always scroll to the latest content, even if the content rendered after the scroll-to-start animation already began.
For 1., we leverage the XAML CanBeScrollAnchor property on each top-level item in the list view. While this is an imperfect solution (re-rendering of this content while in the view port can result in view port shifts as new content renders above), it is a good trade-off of performance and functionality.
For 2., we leverage the XAML HorizontalAnchorRatio and VerticalAnchorRatio properties. XAML has a special case for inverted
lists when setting these property values to 1.0. It instructs XAML to synchronously scroll to and render new content when scrolled to the bottom edge of the ScrollViewer.
For 3., we leverage Yoga's implementation of flexDirection: column-reverse and flexDirection: row-reverse to ensure content is rendered from bottom to top.
For 4., we implemented ScrollViewViewChanger to continuously check if the target scroll offset has changed since starting an animated scroll-to-end operation. If the target scroll offset no longer matches the scrollable extent of the ScrollViewer, we update the target offset by calling ChangeView again.
There are two known limitations to this change:
- This implementation does not yet support list virtualization, so you must set
disableVirtualization={true}when you setinverted={true} - This implementation does not yet support the visual overlay for
debug={true}, which is really only useful when virtualization is enabled.
Fixes #4098
Testing
Note: in the video, I'm using the scroll wheel and the ScrollView is scrolling in the correct direction:
https://user-images.githubusercontent.com/1106239/182451523-f0ed3002-2dc8-4aaa-939f-afb0dbea176e.mp4
Microsoft Reviewers: Open in CodeFlow
Also pushed some of the platform agnostic changes to VirtualizedList in RN Core: https://github.com/facebook/react-native/pull/32038
This is not super immediately clear, but @react-native-windows/virtualized-list is used for more than just XAML. It is also used for the Office internal NetUI platform.
What it basically means is that we can't assume its behavior only affects XAML. It is more of a staging package for upstream. We can do special behavior using Platform.OS checks though, or through other Platform APIs.
Bulk marking PRs without activity in the last 14 days as needing author feedback. This helps keep all active PRs visible on the first page of PRs. msftbot will close this PR in two weeks if the label is not removed. Please feel free to remove the label if you are still actively working on this.
This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within 7 days of this comment.
Note, switching from inverted=false to inverted=true is not really a supported scenario, so in order to test, you'll have to modify the FlatList-basic.js example from RNTester to default to inverted on first render.
I believe decision was that this PR will not be taken into RNW on Paper. Tagging with Needs Attention to confirm decision in triage and close.
Discussed in PR triage. Decision to not take change for Paper architecture still stands.