Skip to content

Commit

Permalink
Add new preserveScrollMomentum prop
Browse files Browse the repository at this point in the history
  • Loading branch information
huderlem authored and yayvery committed Feb 24, 2024
1 parent 7e2b0b0 commit 719252d
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function createBottomSheetScrollableComponent<T, P>(
progressViewOffset,
refreshControl,
scrollBuffer,
preserveScrollMomentum,
// events
onScroll,
onScrollBeginDrag,
Expand All @@ -59,7 +60,8 @@ export function createBottomSheetScrollableComponent<T, P>(
onScroll,
onScrollBeginDrag,
onScrollEndDrag,
scrollBuffer
scrollBuffer,
preserveScrollMomentum
);
const {
enableContentPanningGesture,
Expand All @@ -73,8 +75,7 @@ export function createBottomSheetScrollableComponent<T, P>(
//#region variables
const scrollableAnimatedProps = useAnimatedProps(
() => ({
decelerationRate:
SCROLLABLE_DECELERATION_RATE_MAPPER[animatedScrollableState.value],
...(preserveScrollMomentum ? {} : {decelerationRate: SCROLLABLE_DECELERATION_RATE_MAPPER[animatedScrollableState.value]}),
showsVerticalScrollIndicator: showsVerticalScrollIndicator
? animatedScrollableState.value === SCROLLABLE_STATE.UNLOCKED
: showsVerticalScrollIndicator,
Expand Down Expand Up @@ -127,6 +128,7 @@ export function createBottomSheetScrollableComponent<T, P>(
scrollableContentOffsetY,
onRefresh !== undefined,
scrollBuffer,
preserveScrollMomentum,
focusHook
);
//#endregion
Expand Down
5 changes: 5 additions & 0 deletions src/components/bottomSheetScrollable/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ export interface BottomSheetScrollableProps {
* An initial scroll buffer to prevent the bottom sheet from immediately following the scroll gesture.
*/
scrollBuffer?: number;

/**
* Whether or not to preserve scroll momentum when expanding a scrollable bottom sheet component.
*/
preserveScrollMomentum?: boolean;
}

export type ScrollableProps<T> =
Expand Down
42 changes: 25 additions & 17 deletions src/hooks/useScrollEventsHandlersDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export type ScrollEventContextType = {
export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
scrollableRef,
scrollableContentOffsetY,
scrollBuffer
scrollBuffer,
preserveScrollMomentum
) => {
// hooks
const {
Expand All @@ -25,6 +26,7 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
isScrollableLocked,
} = useBottomSheetInternal();
const awaitingFirstScroll = useSharedValue(false);
const scrollEnded = useSharedValue(false);

//#region callbacks
const handleOnScroll: ScrollEventHandlerCallbackType<ScrollEventContextType> =
Expand All @@ -36,9 +38,9 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
* handleOnBeginDrag, and the scrollable shouldn't be locked when scrolling back to the
* start of the list.
*/
if (scrollBuffer && awaitingFirstScroll.value && !isScrollableLocked.value) {
if ((preserveScrollMomentum || scrollBuffer) && awaitingFirstScroll.value && !isScrollableLocked.value) {
const isScrollingTowardsBottom = context.initialContentOffsetY < event.contentOffset.y;
if (isScrollingTowardsBottom && event.contentOffset.y > scrollBuffer && context.shouldLockInitialPosition) {
if (isScrollingTowardsBottom && event.contentOffset.y > (scrollBuffer ?? 0) && context.shouldLockInitialPosition) {
isScrollableLocked.value = true;
animatedScrollableState.value = SCROLLABLE_STATE.LOCKED;
context.shouldLockInitialPosition = true;
Expand All @@ -58,12 +60,14 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
}

if (animatedScrollableState.value === SCROLLABLE_STATE.LOCKED) {
const lockPosition = context.shouldLockInitialPosition
? context.initialContentOffsetY ?? 0
: 0;
// @ts-ignore
scrollTo(scrollableRef, 0, lockPosition, false);
scrollableContentOffsetY.value = lockPosition;
if (!(preserveScrollMomentum && scrollEnded.value)) {
const lockPosition = context.shouldLockInitialPosition
? context.initialContentOffsetY ?? 0
: 0;
// @ts-ignore
scrollTo(scrollableRef, 0, lockPosition, false);
scrollableContentOffsetY.value = lockPosition;
}
return;
}
},
Expand All @@ -82,6 +86,7 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
rootScrollableContentOffsetY.value = y;
context.initialContentOffsetY = y;
awaitingFirstScroll.value = true;
scrollEnded.value = false;

if (scrollBuffer) {
if (y <= 0 && (
Expand All @@ -93,7 +98,7 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
isScrollableLocked.value = false;
}
} else {
isScrollableLocked.value = true;
isScrollableLocked.value = preserveScrollMomentum ? y <= 0 : true;
}

/**
Expand All @@ -118,8 +123,9 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
);
const handleOnEndDrag: ScrollEventHandlerCallbackType<ScrollEventContextType> =
useWorkletCallback(
({ contentOffset: { y } }, context) => {
({ contentOffset: { y }}, context) => {
awaitingFirstScroll.value = false;
scrollEnded.value = true;
if (animatedScrollableState.value === SCROLLABLE_STATE.LOCKED) {
const lockPosition = context.shouldLockInitialPosition
? context.initialContentOffsetY ?? 0
Expand All @@ -146,12 +152,14 @@ export const useScrollEventsHandlersDefault: ScrollEventsHandlersHookType = (
useWorkletCallback(
({ contentOffset: { y } }, context) => {
if (animatedScrollableState.value === SCROLLABLE_STATE.LOCKED) {
const lockPosition = context.shouldLockInitialPosition
? context.initialContentOffsetY ?? 0
: 0;
// @ts-ignore
scrollTo(scrollableRef, 0, lockPosition, false);
scrollableContentOffsetY.value = 0;
if (!(preserveScrollMomentum && scrollEnded.value)) {
const lockPosition = context.shouldLockInitialPosition
? context.initialContentOffsetY ?? 0
: 0;
// @ts-ignore
scrollTo(scrollableRef, 0, lockPosition, false);
scrollableContentOffsetY.value = 0;
}
return;
}
if (animatedAnimationState.value !== ANIMATION_STATE.RUNNING) {
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useScrollHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export const useScrollHandler = (
onScroll?: ScrollableEvent,
onScrollBeginDrag?: ScrollableEvent,
onScrollEndDrag?: ScrollableEvent,
scrollBuffer?: number
scrollBuffer?: number,
preserveScrollMomentum?: boolean
) => {
// refs
const scrollableRef = useAnimatedRef<Scrollable>();
Expand All @@ -28,7 +29,7 @@ export const useScrollHandler = (
handleOnEndDrag = noop,
handleOnMomentumEnd = noop,
handleOnMomentumBegin = noop,
} = useScrollEventsHandlers(scrollableRef, scrollableContentOffsetY, scrollBuffer);
} = useScrollEventsHandlers(scrollableRef, scrollableContentOffsetY, scrollBuffer, preserveScrollMomentum);

// callbacks
const scrollHandler = useAnimatedScrollHandler(
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/useScrollableSetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const useScrollableSetter = (
contentOffsetY: Animated.SharedValue<number>,
refreshable: boolean,
scrollBuffer: number | undefined,
preserveScrollMomentum: boolean | undefined,
useFocusHook = useEffect
) => {
// hooks
Expand All @@ -30,7 +31,7 @@ export const useScrollableSetter = (
rootScrollableContentOffsetY.value = contentOffsetY.value;
animatedScrollableType.value = type;
isScrollableRefreshable.value = refreshable;
isScrollableLocked.value = !scrollBuffer;
isScrollableLocked.value = !preserveScrollMomentum && !scrollBuffer;
isContentHeightFixed.value = false;

// set current scrollable ref
Expand Down
1 change: 1 addition & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export type ScrollEventsHandlersHookType = (
ref: React.RefObject<Scrollable>,
contentOffsetY: SharedValue<number>,
scrollBuffer: number | undefined,
preserveScrollMomentum: boolean | undefined,
) => {
handleOnScroll?: ScrollEventHandlerCallbackType;
handleOnBeginDrag?: ScrollEventHandlerCallbackType;
Expand Down

0 comments on commit 719252d

Please sign in to comment.