@@ -25,8 +25,7 @@ import useDebugHelpers from './useDebugHelpers';
2525import {
2626 calculateRawProgressHorizontal ,
2727 calculateRawProgressVertical ,
28- clampDistanceHorizontal ,
29- clampDistanceVertical
28+ clampDistance
3029} from './utils' ;
3130
3231// Maximum elapsed time multiplier to prevent excessive scrolling distances when app lags
@@ -102,6 +101,18 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
102101 } ;
103102} ) ;
104103
104+ type StateContextType = {
105+ targetScrollOffset : null | number ;
106+ prevContainerOffset : null | number ;
107+ lastUpdateTimestamp : null | number ;
108+ } ;
109+
110+ const INITIAL_STATE : StateContextType = {
111+ lastUpdateTimestamp : null ,
112+ prevContainerOffset : null ,
113+ targetScrollOffset : null
114+ } ;
115+
105116type AutoScrollUpdaterProps = Omit <
106117 AutoScrollSettings ,
107118 'autoScrollDirection' | 'autoScrollEnabled'
@@ -129,8 +140,7 @@ function AutoScrollUpdater({
129140 useCommonValuesContext ( ) ;
130141
131142 const progress = useMutableValue ( 0 ) ;
132- const lastUpdateTimestamp = useMutableValue < null | number > ( null ) ;
133- const targetScrollOffset = useMutableValue < null | number > ( null ) ;
143+ const context = useMutableValue < StateContextType > ( INITIAL_STATE ) ;
134144
135145 const scrollAxis = isVertical ? 'y' : 'x' ;
136146 const activationOffset = toPair ( autoScrollActivationOffset ) ;
@@ -158,29 +168,26 @@ function AutoScrollUpdater({
158168 ) ;
159169 }
160170
161- let calculateRawProgress , clampDistance ;
162- if ( isVertical ) {
163- calculateRawProgress = calculateRawProgressVertical ;
164- clampDistance = clampDistanceVertical ;
165- } else {
166- calculateRawProgress = calculateRawProgressHorizontal ;
167- clampDistance = clampDistanceHorizontal ;
168- }
171+ const calculateRawProgress = isVertical
172+ ? calculateRawProgressVertical
173+ : calculateRawProgressHorizontal ;
169174
170175 useAnimatedReaction (
171176 ( ) => {
177+ const ctx = context . value ;
172178 let position = touchPosition . value ?. [ scrollAxis ] ?? null ;
173- if ( position !== null && targetScrollOffset . value !== null ) {
179+ if ( position !== null && ctx . targetScrollOffset !== null ) {
174180 // Sometimes the scroll distance is so small that the scrollTo takes
175181 // no effect. To handle this case, we have to update the position
176182 // of the view used to determine the progress, even if the actual
177183 // position of the view is not changed (because of too small scroll distance).
178- position += targetScrollOffset . value - currentScrollOffset . value ;
184+ position += ctx . targetScrollOffset - currentScrollOffset . value ;
179185 }
180186 return position ;
181187 } ,
182188 position => {
183189 if ( position === null ) {
190+ context . value . targetScrollOffset = null ;
184191 debug ?. hideDebugViews ?.( ) ;
185192 return ;
186193 }
@@ -202,7 +209,7 @@ function AutoScrollUpdater({
202209 ) ;
203210
204211 if ( progress . value === 0 ) {
205- targetScrollOffset . value = null ;
212+ context . value . targetScrollOffset = null ;
206213 }
207214
208215 debug ?. updateDebugRects ?.(
@@ -223,26 +230,46 @@ function AutoScrollUpdater({
223230 return ;
224231 }
225232
233+ const ctx = context . value ;
226234 const pendingDistance =
227- targetScrollOffset . value !== null
228- ? targetScrollOffset . value - currentScrollOffset . value
235+ ctx . targetScrollOffset !== null
236+ ? ctx . targetScrollOffset - currentScrollOffset . value
229237 : 0 ;
230238
239+ const containerOffset = isVertical
240+ ? scrollableMeasurements . pageY - containerMeasurements . pageY
241+ : scrollableMeasurements . pageX - containerMeasurements . pageX ;
242+ const scrollableCrossSize = isVertical
243+ ? scrollableMeasurements . height
244+ : scrollableMeasurements . width ;
245+
246+ if (
247+ pendingDistance !== 0 &&
248+ containerOffset === ctx . prevContainerOffset
249+ ) {
250+ // Return if measurements haven't been updated yet (we scroll based on the
251+ // relative position of the container in the ScrollView so we have to ensure
252+ // that the last update is already applied)
253+ return ;
254+ }
255+
231256 const clampedDistance = clampDistance (
232257 distance + pendingDistance ,
233- containerMeasurements ,
234- scrollableMeasurements ,
258+ containerOffset ,
259+ scrollableCrossSize ,
235260 bounds ,
236261 maxOverscroll
237262 ) ;
238263
239264 const targetOffset = currentScrollOffset . value + clampedDistance ;
240- targetScrollOffset . value = targetOffset ;
265+
266+ ctx . targetScrollOffset = targetOffset ;
241267
242268 if ( Math . abs ( clampedDistance ) < 1 ) {
243269 return ;
244270 }
245271
272+ ctx . prevContainerOffset = containerOffset ;
246273 scrollTo (
247274 scrollableRef ,
248275 isVertical ? 0 : targetOffset ,
@@ -251,13 +278,12 @@ function AutoScrollUpdater({
251278 ) ;
252279 } ,
253280 [
281+ context ,
254282 currentScrollOffset ,
255- targetScrollOffset ,
256283 isVertical ,
257284 scrollableRef ,
258285 containerRef ,
259286 contentAxisBounds ,
260- clampDistance ,
261287 maxOverscroll
262288 ]
263289 ) ;
@@ -269,8 +295,9 @@ function AutoScrollUpdater({
269295 return ;
270296 }
271297
272- lastUpdateTimestamp . value ??= timestamp ;
273- const elapsedTime = timestamp - lastUpdateTimestamp . value ;
298+ const ctx = context . value ;
299+ ctx . lastUpdateTimestamp ??= timestamp ;
300+ const elapsedTime = timestamp - ctx . lastUpdateTimestamp ;
274301 if ( elapsedTime < autoScrollInterval ) {
275302 return ;
276303 }
@@ -281,7 +308,7 @@ function AutoScrollUpdater({
281308 MIN_ELAPSED_TIME_CAP
282309 ) ;
283310 const cappedElapsedTime = Math . min ( elapsedTime , maxElapsedTime ) ;
284- lastUpdateTimestamp . value = timestamp ;
311+ ctx . lastUpdateTimestamp = timestamp ;
285312
286313 const velocity = interpolate (
287314 progress . value ,
@@ -294,13 +321,13 @@ function AutoScrollUpdater({
294321 scrollBy ( distance , animateScrollTo ) ;
295322 } ,
296323 [
324+ context ,
297325 scrollBy ,
298326 maxStartVelocity ,
299327 maxEndVelocity ,
300328 progress ,
301329 autoScrollInterval ,
302- animateScrollTo ,
303- lastUpdateTimestamp
330+ animateScrollTo
304331 ]
305332 ) ;
306333
@@ -318,8 +345,10 @@ function AutoScrollUpdater({
318345 active => {
319346 if ( active ) {
320347 dragStartScrollOffset . value = currentScrollOffset . value ;
321- lastUpdateTimestamp . value = null ;
322- targetScrollOffset . value = null ;
348+ const ctx = context . value ;
349+ ctx . lastUpdateTimestamp = null ;
350+ ctx . targetScrollOffset = null ;
351+ ctx . prevContainerOffset = null ;
323352 runOnJS ( toggleFrameCallback ) ( true ) ;
324353 } else {
325354 dragStartScrollOffset . value = null ;
0 commit comments