Simplify the implementation

I was trying to be too smart and this was causing the current page event to lag behind if you continuously drag. Better to let the library do its job.
This commit is contained in:
Dan Abramov
2024-11-30 02:52:44 +00:00
parent 5e098fa609
commit 9557eee808
2 changed files with 13 additions and 41 deletions
+2 -30
View File
@@ -76,23 +76,13 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
[pagerView], [pagerView],
) )
const pendingPage = useSharedValue(selectedPage)
const dragPage = useSharedValue(selectedPage)
const dragProgress = useSharedValue(0)
const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle') const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
const dragProgress = useSharedValue(selectedPage)
const handlePageScroll = usePagerHandlers( const handlePageScroll = usePagerHandlers(
// These events don't fire exactly the same way on Android and iOS.
// In these handlers we normalize the behavior to have consistent output values.
{ {
onPageScroll(e: PagerViewOnPageScrollEventData) { onPageScroll(e: PagerViewOnPageScrollEventData) {
'worklet' 'worklet'
let {position, offset} = e dragProgress.set(e.offset + e.position)
if (offset !== 0) {
// Normalize so that we always track the offset according to the
// last settled page from the application point of view.
const offsetFromPage = offset + (position - dragPage.value)
dragProgress.set(offsetFromPage)
}
}, },
onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) { onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
'worklet' 'worklet'
@@ -103,27 +93,10 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
} }
dragState.set(e.pageScrollState) dragState.set(e.pageScrollState)
parentOnPageScrollStateChanged?.(e.pageScrollState) parentOnPageScrollStateChanged?.(e.pageScrollState)
if (e.pageScrollState === 'idle') {
// This is a good time to update the last known settled page.
const page = pendingPage.get()
dragPage.set(page)
dragProgress.set(0)
runOnJS(onPageSelectedJSThread)(page)
}
}, },
onPageSelected(e: PagerViewOnPageSelectedEventData) { onPageSelected(e: PagerViewOnPageSelectedEventData) {
'worklet' 'worklet'
// We don't usually emit the "page selected" event here because it fires
// prematurely on Android. We'll emit it in the idle state handler above.
pendingPage.set(e.position)
if (dragState.value === 'idle') {
// However, if we already *are* idle, this event is caused by the initial state.
// Let's fire it here since there will be no scroll state change event later.
const page = e.position
dragPage.set(page)
dragProgress.set(0)
runOnJS(onPageSelectedJSThread)(e.position) runOnJS(onPageSelectedJSThread)(e.position)
}
}, },
}, },
[parentOnPageScrollStateChanged], [parentOnPageScrollStateChanged],
@@ -135,7 +108,6 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
selectedPage, selectedPage,
onSelect: onTabBarSelect, onSelect: onTabBarSelect,
dragGesture: { dragGesture: {
dragPage,
dragProgress, dragProgress,
dragState, dragState,
}, },
+10 -10
View File
@@ -35,20 +35,20 @@ export function TabBar({
const containerSize = useSharedValue(0) const containerSize = useSharedValue(0)
const scrollX = useSharedValue(0) const scrollX = useSharedValue(0)
const itemsLength = items.length const itemsLength = items.length
const {dragPage, dragProgress, dragState} = dragGesture const {dragProgress, dragState} = dragGesture
// When you swipe the pager, the tabbar should scroll automatically // When you swipe the pager, the tabbar should scroll automatically
// as you're dragging the page and then even during deceleration. // as you're dragging the page and then even during deceleration.
useAnimatedReaction( useAnimatedReaction(
() => dragPage.get() + dragProgress.get(), () => dragProgress.get(),
(nextValue, prevValue) => { (nextProgress, prevProgress) => {
if ( if (
nextValue !== prevValue && nextProgress !== prevProgress &&
dragState.value !== 'idle' && dragState.value !== 'idle' &&
isSyncingScroll.get() === true isSyncingScroll.get() === true
) { ) {
const offsetPerPage = contentSize.get() - containerSize.get() const offsetPerPage = contentSize.get() - containerSize.get()
const offset = (nextValue / (itemsLength - 1)) * offsetPerPage const offset = (nextProgress / (itemsLength - 1)) * offsetPerPage
scrollTo(scrollElRef, offset, 0, false) scrollTo(scrollElRef, offset, 0, false)
return return
} }
@@ -69,8 +69,8 @@ export function TabBar({
isSyncingScroll.get() === false isSyncingScroll.get() === false
) { ) {
const offsetPerPage = contentSize.get() - containerSize.get() const offsetPerPage = contentSize.get() - containerSize.get()
const value = dragPage.get() + dragProgress.get() const progress = dragProgress.get()
const offset = (value / (itemsLength - 1)) * offsetPerPage const offset = (progress / (itemsLength - 1)) * offsetPerPage
scrollTo(scrollElRef, offset, 0, true) scrollTo(scrollElRef, offset, 0, true)
isSyncingScroll.set(true) isSyncingScroll.set(true)
} }
@@ -84,8 +84,8 @@ export function TabBar({
'worklet' 'worklet'
if (isSyncingScroll.get() === true) { if (isSyncingScroll.get() === true) {
const offsetPerPage = contentSize.get() - containerSize.get() const offsetPerPage = contentSize.get() - containerSize.get()
const valueDiff = index - dragPage.get() const progressDiff = index - dragProgress.get()
const offsetDiff = (valueDiff / (itemsLength - 1)) * offsetPerPage const offsetDiff = (progressDiff / (itemsLength - 1)) * offsetPerPage
const offset = scrollX.get() + offsetDiff const offset = scrollX.get() + offsetDiff
scrollTo(scrollElRef, offset, 0, true) scrollTo(scrollElRef, offset, 0, true)
} }
@@ -98,7 +98,7 @@ export function TabBar({
itemsLength, itemsLength,
scrollElRef, scrollElRef,
scrollX, scrollX,
dragPage, dragProgress,
], ],
) )