@@ -10,7 +10,6 @@ import {
10
10
useSharedValue
11
11
} from 'react-native-reanimated' ;
12
12
13
- import { OFFSET_EPS } from '../../constants' ;
14
13
import { useDebugContext } from '../../debug' ;
15
14
import { useAnimatableValue } from '../../hooks' ;
16
15
import type { AutoScrollContextType , AutoScrollSettings } from '../../types' ;
@@ -31,28 +30,21 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
31
30
autoScrollSpeed,
32
31
scrollableRef
33
32
} ) => {
34
- const {
35
- activeAnimationProgress,
36
- activeItemKey,
37
- containerRef,
38
- itemDimensions,
39
- touchPosition
40
- } = useCommonValuesContext ( ) ;
33
+ const { activeItemKey, containerRef, itemDimensions, touchPosition } =
34
+ useCommonValuesContext ( ) ;
41
35
const debugContext = useDebugContext ( ) ;
42
36
43
37
const debugRects = debugContext ?. useDebugRects ( [ 'top' , 'bottom' ] ) ;
44
38
const debugLine = debugContext ?. useDebugLine ( ) ;
45
39
46
40
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
47
41
const scrollOffset = useScrollViewOffset ( scrollableRef ) ;
48
- const targetScrollOffset = useSharedValue < null | number > ( null ) ;
49
42
const dragStartScrollOffset = useAnimatableValue < null | number > ( null ) ;
50
- const startContainerPageY = useSharedValue < null | number > ( null ) ;
51
- const prevScrollToOffset = useSharedValue < null | number > ( null ) ;
43
+ const dragScrollOffsetDiff = useSharedValue ( 0 ) ;
52
44
53
45
const activeItemHeight = useDerivedValue ( ( ) => {
54
46
const key = activeItemKey . value ;
55
- return ( key ? itemDimensions . value [ key ] ?. height : null ) ?? null ;
47
+ return key && itemDimensions . value [ key ] ?. height ;
56
48
} ) ;
57
49
const offsetThreshold = useAnimatableValue (
58
50
autoScrollActivationOffset ,
@@ -68,39 +60,86 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
68
60
69
61
const isFrameCallbackActive = useSharedValue ( false ) ;
70
62
63
+ const hideDebugViews = useCallback ( ( ) => {
64
+ 'worklet' ;
65
+ debugRects ?. top ?. hide ( ) ;
66
+ debugRects ?. bottom ?. hide ( ) ;
67
+ debugLine ?. hide ( ) ;
68
+ } , [ debugLine , debugRects ] ) ;
69
+
71
70
// SMOOTH SCROLL POSITION UPDATER
72
71
// Updates the scroll position smoothly
73
72
// (quickly at first, then slower if the remaining distance is small)
74
73
const frameCallback = useFrameCallback ( ( ) => {
75
- const targetOffset = targetScrollOffset . value ;
76
- if ( ! isFrameCallbackActive . value || targetOffset === null ) {
74
+ if (
75
+ activeItemHeight . value === null ||
76
+ touchPosition . value === null ||
77
+ dragStartScrollOffset . value === null
78
+ ) {
79
+ hideDebugViews ( ) ;
77
80
return ;
78
81
}
79
- const currentOffset = scrollOffset . value ;
80
- const diff = targetOffset - currentOffset ;
81
82
82
- if ( Math . abs ( diff ) < OFFSET_EPS ) {
83
- targetScrollOffset . value = null ;
83
+ const scrollableMeasurements = measure ( scrollableRef ) ;
84
+ const containerMeasurements = measure ( containerRef ) ;
85
+
86
+ if ( ! scrollableMeasurements || ! containerMeasurements ) {
87
+ hideDebugViews ( ) ;
84
88
return ;
85
89
}
86
90
87
- const direction = diff > 0 ? 1 : - 1 ;
88
- const step = speed . value * direction * Math . sqrt ( Math . abs ( diff ) ) ;
89
- const nextOffset =
90
- targetOffset > currentOffset
91
- ? Math . min ( currentOffset + step , targetOffset )
92
- : Math . max ( currentOffset + step , targetOffset ) ;
91
+ const scrollToOffset =
92
+ dragStartScrollOffset . value + dragScrollOffsetDiff . value ;
93
+ if ( Math . abs ( scrollOffset . value - scrollToOffset ) > 1 ) {
94
+ console . log ( 'scrollToOffset' , scrollToOffset , scrollOffset . value ) ;
95
+ scrollTo ( scrollableRef , 0 , scrollToOffset , false ) ;
96
+ }
93
97
94
- if (
95
- Math . abs ( nextOffset - currentOffset ) < 0.1 * OFFSET_EPS ||
96
- prevScrollToOffset . value === nextOffset
97
- ) {
98
- targetScrollOffset . value = null ;
99
- return ;
98
+ const threshold = offsetThreshold . value ;
99
+ const touchOffset = touchPosition . value . y ;
100
+ const { height : sH , pageY : sY } = scrollableMeasurements ;
101
+ const { height : cH , pageY : cY } = containerMeasurements ;
102
+
103
+ if ( debugRects ) {
104
+ debugRects . top . set ( {
105
+ ...DEBUG_COLORS ,
106
+ height : threshold . top ,
107
+ y : sY - cY
108
+ } ) ;
109
+ debugRects . bottom . set ( {
110
+ ...DEBUG_COLORS ,
111
+ height : threshold . bottom ,
112
+ positionOrigin : 'bottom' ,
113
+ y : sY - cY + sH
114
+ } ) ;
115
+ }
116
+ if ( debugLine ) {
117
+ debugLine . set ( {
118
+ color : DEBUG_COLORS . backgroundColor ,
119
+ y : touchOffset
120
+ } ) ;
100
121
}
101
122
102
- scrollTo ( scrollableRef , 0 , nextOffset , false ) ;
103
- prevScrollToOffset . value = nextOffset ;
123
+ const topDistance = sY + threshold . top - cY ;
124
+ const bottomDistance = cY + cH - ( sY + sH - threshold . bottom ) ;
125
+
126
+ const topOverflow = sY + threshold . top - ( cY + touchOffset ) ;
127
+ const bottomOverflow = cY + touchOffset - ( sY + sH - threshold . bottom ) ;
128
+
129
+ const scrollOffsetDiff = scrollOffset . value - dragStartScrollOffset . value ;
130
+ // Scroll up
131
+ if ( topDistance > 0 && topOverflow > 0 ) {
132
+ console . log ( 'up dragScrollOffsetDiff.value' , dragScrollOffsetDiff . value ) ;
133
+ dragScrollOffsetDiff . value = scrollOffsetDiff - 0.05 * topOverflow ;
134
+ }
135
+ // Scroll down
136
+ else if ( bottomDistance > 0 && bottomOverflow > 0 ) {
137
+ console . log (
138
+ 'down dragScrollOffsetDiff.value' ,
139
+ dragScrollOffsetDiff . value
140
+ ) ;
141
+ dragScrollOffsetDiff . value = scrollOffsetDiff + 0.05 * bottomOverflow ;
142
+ }
104
143
} , false ) ;
105
144
106
145
const toggleFrameCallback = useCallback (
@@ -111,127 +150,32 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
111
150
// Enable/disable frame callback
112
151
useAnimatedReaction (
113
152
( ) => ( {
114
- isEnabled : enabled . value ,
115
- itemKey : activeItemKey . value ,
116
- progress : activeAnimationProgress . value
153
+ activeKey : activeItemKey . value ,
154
+ isEnabled : enabled . value
117
155
} ) ,
118
- ( { isEnabled, itemKey, progress } ) => {
119
- const shouldBeEnabled = isEnabled && itemKey !== null ;
120
- if (
121
- isFrameCallbackActive . value === shouldBeEnabled ||
122
- ( itemKey !== null && progress < 0.5 )
123
- ) {
156
+ ( { activeKey, isEnabled } ) => {
157
+ const shouldBeEnabled = isEnabled && activeKey !== null ;
158
+ if ( isFrameCallbackActive . value === shouldBeEnabled ) {
124
159
return ;
125
160
}
126
- targetScrollOffset . value = null ;
127
- startContainerPageY . value = null ;
128
- prevScrollToOffset . value = null ;
129
161
runOnJS ( toggleFrameCallback ) ( shouldBeEnabled ) ;
130
162
isFrameCallbackActive . value = shouldBeEnabled ;
131
163
}
132
164
) ;
133
165
134
- // AUTO SCROLL HANDLER
135
- // Automatically scrolls the container when the active item is near the edge
136
- useAnimatedReaction (
137
- ( ) => {
138
- if (
139
- ! enabled . value ||
140
- activeItemHeight . value === null ||
141
- ! touchPosition . value
142
- ) {
143
- return null ;
144
- }
145
-
146
- return {
147
- itemHeight : activeItemHeight . value ,
148
- threshold : offsetThreshold . value ,
149
- touchOffset : touchPosition . value . y
150
- } ;
151
- } ,
152
- props => {
153
- const hideDebugViews = ( ) => {
154
- debugRects ?. top ?. hide ( ) ;
155
- debugRects ?. bottom ?. hide ( ) ;
156
- debugLine ?. hide ( ) ;
157
- } ;
158
-
159
- if ( ! props ) {
160
- hideDebugViews ( ) ;
161
- return ;
162
- }
163
-
164
- const scrollableMeasurements = measure ( scrollableRef ) ;
165
- const containerMeasurements = measure ( containerRef ) ;
166
-
167
- if (
168
- ! scrollableMeasurements ||
169
- ! containerMeasurements ||
170
- dragStartScrollOffset . value === null
171
- ) {
172
- hideDebugViews ( ) ;
173
- return ;
174
- }
175
-
176
- const { threshold, touchOffset } = props ;
177
- const { height : sH , pageY : sY } = scrollableMeasurements ;
178
- const { height : cH , pageY : cY } = containerMeasurements ;
179
-
180
- if ( startContainerPageY . value === null ) {
181
- startContainerPageY . value = cY ;
182
- }
183
-
184
- const topDistance = sY + threshold . top - cY ;
185
- const bottomDistance = cY + cH - ( sY + sH - threshold . bottom ) ;
186
-
187
- const topOverflow = sY + threshold . top - ( cY + touchOffset ) ;
188
- const bottomOverflow = cY + touchOffset - ( sY + sH - threshold . bottom ) ;
189
-
190
- if ( debugRects ) {
191
- debugRects . top . set ( {
192
- ...DEBUG_COLORS ,
193
- height : threshold . top ,
194
- y : sY - cY
195
- } ) ;
196
- debugRects . bottom . set ( {
197
- ...DEBUG_COLORS ,
198
- height : threshold . bottom ,
199
- positionOrigin : 'bottom' ,
200
- y : sY - cY + sH
201
- } ) ;
202
- }
203
- if ( debugLine ) {
204
- debugLine . set ( {
205
- color : DEBUG_COLORS . backgroundColor ,
206
- y : touchOffset
207
- } ) ;
208
- }
209
-
210
- const deltaY = startContainerPageY . value - cY ;
211
- const offsetY = dragStartScrollOffset . value + deltaY ;
212
- // Scroll up
213
- if ( topDistance > 0 && topOverflow > 0 ) {
214
- targetScrollOffset . value = offsetY - Math . min ( topOverflow , topDistance ) ;
215
- }
216
- // Scroll down
217
- else if ( bottomDistance > 0 && bottomOverflow > 0 ) {
218
- targetScrollOffset . value =
219
- offsetY + Math . min ( bottomOverflow , bottomDistance ) ;
220
- }
221
- }
222
- ) ;
223
-
224
166
const updateStartScrollOffset = useCallback (
225
167
( providedOffset ?: null | number ) => {
226
168
'worklet' ;
169
+ dragScrollOffsetDiff . value = 0 ;
227
170
dragStartScrollOffset . value =
228
- providedOffset === undefined ? scrollOffset . value : providedOffset ;
171
+ providedOffset !== undefined ? providedOffset : scrollOffset . value ;
229
172
} ,
230
- [ dragStartScrollOffset , scrollOffset ]
173
+ [ dragScrollOffsetDiff , dragStartScrollOffset , scrollOffset ]
231
174
) ;
232
175
233
176
return {
234
177
value : {
178
+ dragScrollOffsetDiff,
235
179
dragStartScrollOffset,
236
180
scrollOffset,
237
181
updateStartScrollOffset
0 commit comments