11import type { PropsWithChildren } from "react" ;
2- import React from "react" ;
2+ import React , { useCallback , useMemo } from "react" ;
33import type { StyleProp , ViewStyle } from "react-native" ;
44import type { GestureStateChangeEvent , PanGestureHandlerEventPayload } from "react-native-gesture-handler" ;
55import {
@@ -70,6 +70,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
7070 const scrollEndTranslation = useSharedValue ( 0 ) ;
7171 const scrollEndVelocity = useSharedValue ( 0 ) ;
7272 const containerRef = useAnimatedRef < Animated . View > ( ) ;
73+ const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number" ;
7374
7475 // Get the limit of the scroll.
7576 const getLimit = React . useCallback ( ( ) => {
@@ -121,7 +122,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
121122 let finalTranslation : number = withDecay ( { velocity, deceleration : 0.999 } ) ;
122123
123124 // If the distance of the swipe exceeds the max scroll distance, keep the view at the current position
124- if ( typeof maxScrollDistancePerSwipe === "number" && Math . abs ( scrollEndTranslation . value ) > maxScrollDistancePerSwipe ) {
125+ if ( maxScrollDistancePerSwipeIsSet && Math . abs ( scrollEndTranslation . value ) > maxScrollDistancePerSwipe ) {
125126 finalTranslation = origin ;
126127 }
127128 else {
@@ -178,6 +179,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
178179 scrollEndVelocity . value ,
179180 maxScrollDistancePerSwipe ,
180181 scrollEndTranslation . value ,
182+ maxScrollDistancePerSwipeIsSet ,
181183 ] ,
182184 ) ;
183185
@@ -245,7 +247,19 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
245247 [ pagingEnabled , resetBoundary ] ,
246248 ) ;
247249
248- const onGestureBegin = ( ) => {
250+ function withProcessTranslation ( translation : number ) {
251+ "worklet" ;
252+
253+ if ( ! infinite && ! overscrollEnabled ) {
254+ const limit = getLimit ( ) ;
255+ const sign = Math . sign ( translation ) ;
256+ return sign * Math . max ( 0 , Math . min ( limit , Math . abs ( translation ) ) ) ;
257+ }
258+
259+ return translation ;
260+ }
261+
262+ const onGestureBegin = useCallback ( ( ) => {
249263 "worklet" ;
250264
251265 touching . value = true ;
@@ -257,8 +271,21 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
257271 max . value = getLimit ( ) ;
258272
259273 panOffset . value = translation . value ;
260- } ;
261- const onGestureUpdate = ( e : PanGestureHandlerEventPayload ) => {
274+ } , [
275+ max ,
276+ size ,
277+ maxPage ,
278+ infinite ,
279+ touching ,
280+ panOffset ,
281+ validStart ,
282+ translation ,
283+ overscrollEnabled ,
284+ getLimit ,
285+ onScrollBegin ,
286+ ] ) ;
287+
288+ const onGestureUpdate = useCallback ( ( e : PanGestureHandlerEventPayload ) => {
262289 "worklet" ;
263290
264291 if ( validStart . value ) {
@@ -282,8 +309,18 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
282309
283310 const translationValue = panOffset . value + panTranslation ;
284311 translation . value = translationValue ;
285- } ;
286- const onGestureFinish = ( e : GestureStateChangeEvent < PanGestureHandlerEventPayload > ) => {
312+ } , [
313+ isHorizontal ,
314+ max ,
315+ panOffset ,
316+ infinite ,
317+ overscrollEnabled ,
318+ translation ,
319+ validStart ,
320+ touching ,
321+ ] ) ;
322+
323+ const onGestureFinish = useCallback ( ( e : GestureStateChangeEvent < PanGestureHandlerEventPayload > ) => {
287324 "worklet" ;
288325
289326 const { velocityX, velocityY, translationX, translationY } = e ;
@@ -294,13 +331,39 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
294331 ? translationX
295332 : translationY ;
296333
297- endWithSpring ( onScrollEnd ) ;
334+ const totalTranslation = scrollEndVelocity . value + scrollEndTranslation . value ;
335+
336+ if ( maxScrollDistancePerSwipeIsSet && Math . abs ( totalTranslation ) > maxScrollDistancePerSwipe ) {
337+ const nextPage = Math . round ( ( panOffset . value + maxScrollDistancePerSwipe * Math . sign ( totalTranslation ) ) / size ) * size ;
338+ translation . value = withSpring ( withProcessTranslation ( nextPage ) , onScrollEnd ) ;
339+ }
340+ else {
341+ endWithSpring ( onScrollEnd ) ;
342+ }
298343
299344 if ( ! infinite )
300345 touching . value = false ;
301- } ;
346+ } , [
347+ size ,
348+ infinite ,
349+ touching ,
350+ panOffset ,
351+ translation ,
352+ isHorizontal ,
353+ scrollEndVelocity ,
354+ scrollEndTranslation ,
355+ maxScrollDistancePerSwipeIsSet ,
356+ maxScrollDistancePerSwipe ,
357+ endWithSpring ,
358+ withSpring ,
359+ onScrollEnd ,
360+ ] ) ;
302361
303- const gesture = Gesture . Pan ( ) . onBegin ( onGestureBegin ) . onUpdate ( onGestureUpdate ) . onEnd ( onGestureFinish ) ;
362+ const gesture = useMemo ( ( ) => Gesture . Pan ( ) . onBegin ( onGestureBegin ) . onUpdate ( onGestureUpdate ) . onEnd ( onGestureFinish ) , [
363+ onGestureBegin ,
364+ onGestureUpdate ,
365+ onGestureFinish ,
366+ ] ) ;
304367 const GestureContainer = enabled ? GestureDetector : React . Fragment ;
305368
306369 return (
0 commit comments