Skip to content

Commit 51ccfad

Browse files
committed
Finish the new auto scroll implementation
1 parent ee1ab65 commit 51ccfad

8 files changed

Lines changed: 57 additions & 81 deletions

File tree

example/app/src/examples/SortableGrid/features/AutoScrollExample.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ function ManyCards({ scrollableRef }: CardsSectionProps) {
155155
renderItem={({ item }) => <GridCard>{item}</GridCard>}
156156
rowGap={spacing.xs}
157157
scrollableRef={scrollableRef}
158-
debug
159158
/>
160159
);
161160
}
@@ -169,7 +168,6 @@ function FewCards({ scrollableRef }: CardsSectionProps) {
169168
renderItem={({ item }) => <GridCard>{item}</GridCard>}
170169
rowGap={spacing.xs}
171170
scrollableRef={scrollableRef}
172-
debug
173171
/>
174172
);
175173
}

packages/react-native-sortables/src/components/SortableFlex.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { memo, useMemo } from 'react';
22
import type { ViewStyle } from 'react-native';
33
import { StyleSheet } from 'react-native';
4-
import { useAnimatedStyle } from 'react-native-reanimated';
4+
import { runOnUI, useAnimatedStyle } from 'react-native-reanimated';
55

66
import { DEFAULT_SORTABLE_FLEX_PROPS } from '../constants';
77
import type { PropsWithDefaults } from '../hooks';
@@ -236,7 +236,7 @@ function SortableFlexComponent({
236236
{...rest}
237237
containerStyle={[baseContainerStyle, animatedContainerStyle]}
238238
itemStyle={styles.styleOverride}
239-
onLayout={handleContainerMeasurement}
239+
onLayout={runOnUI(handleContainerMeasurement)}
240240
/>
241241
);
242242
}

packages/react-native-sortables/src/components/SortableGrid.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useLayoutEffect, useMemo, useRef } from 'react';
22
import type { DimensionValue } from 'react-native';
33
import { StyleSheet } from 'react-native';
44
import type { SharedValue } from 'react-native-reanimated';
5-
import { useAnimatedStyle } from 'react-native-reanimated';
5+
import { runOnUI, useAnimatedStyle } from 'react-native-reanimated';
66

77
import { DEFAULT_SORTABLE_GRID_PROPS, IS_WEB } from '../constants';
88
import type { PropsWithDefaults } from '../hooks';
@@ -249,13 +249,12 @@ function SortableGridComponent<I>({
249249
{...rest}
250250
containerStyle={[styles.gridContainer, animatedInnerStyle]}
251251
itemStyle={itemStyle}
252-
onLayout={(w, h) => {
253-
'worklet';
252+
onLayout={runOnUI((w, h) => {
254253
handleContainerMeasurement(
255254
w - (isVertical && !IS_WEB ? columnGap.value : 0),
256255
h
257256
);
258-
}}
257+
})}
259258
/>
260259
);
261260
}

packages/react-native-sortables/src/components/shared/SortableContainer.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,14 @@ import { memo, useSyncExternalStore } from 'react';
22
import type { StyleProp, ViewStyle } from 'react-native';
33
import Animated, {
44
LinearTransition,
5-
runOnUI,
65
useAnimatedStyle,
76
withTiming
87
} from 'react-native-reanimated';
98

109
import { EMPTY_OBJECT, IS_WEB } from '../../constants';
1110
import { DebugOutlet } from '../../debug';
1211
import type { AnimatedStyleProp } from '../../integrations/reanimated';
13-
import {
14-
useAutoScrollContext,
15-
useCommonValuesContext,
16-
useItemsContext
17-
} from '../../providers';
12+
import { useCommonValuesContext, useItemsContext } from '../../providers';
1813
import type {
1914
DimensionsAnimation,
2015
DropIndicatorSettings,
@@ -58,7 +53,6 @@ export default function SortableContainer({
5853
shouldAnimateLayout,
5954
usesAbsoluteLayout
6055
} = useCommonValuesContext();
61-
const { onContainerLayout } = useAutoScrollContext() ?? {};
6256

6357
const animateWorklet = dimensionsAnimationType === 'worklet';
6458
const animateLayout = dimensionsAnimationType === 'layout';
@@ -122,10 +116,7 @@ export default function SortableContainer({
122116
ref={containerRef}
123117
style={[containerStyle, innerContainerStyle]}
124118
onLayout={({ nativeEvent: { layout } }) =>
125-
runOnUI(() => {
126-
onContainerLayout?.();
127-
onLayout(layout.width, layout.height);
128-
})()
119+
onLayout(layout.width, layout.height)
129120
}>
130121
<ItemsOutlet
131122
itemEntering={itemEntering}

packages/react-native-sortables/src/providers/shared/AutoScrollProvider/AutoScrollProvider.tsx

Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,12 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
103103

104104
const prop = isVertical ? 'pageY' : 'pageX';
105105
const scrollContainerPosition =
106-
scrollableMeasurements[prop] + scrollOffset.value;
106+
scrollableMeasurements[prop] - scrollOffset.value;
107107
const sortableContainerPosition = containerMeasurements[prop];
108108

109-
console.log(containerMeasurements, scrollableMeasurements);
110-
111109
return sortableContainerPosition - scrollContainerPosition;
112110
}, [containerRef, scrollableRef, scrollOffset, isVertical]);
113111

114-
const onContainerLayout = useCallback(() => {
115-
'worklet';
116-
if (context.value) {
117-
context.value.sortableOffset = getSortableOffset() ?? 0;
118-
}
119-
}, [context, getSortableOffset]);
120-
121112
return {
122113
children: (
123114
<>
@@ -140,7 +131,6 @@ const { AutoScrollProvider, useAutoScrollContext } = createProvider(
140131
value: {
141132
contentBounds,
142133
isVerticalScroll: isVertical,
143-
onContainerLayout,
144134
scrollableRef,
145135
scrollBy,
146136
scrollOffsetDiff
@@ -175,8 +165,7 @@ function AutoScrollUpdater({
175165
scrollOffset,
176166
scrollToOffset
177167
}: AutoScrollUpdaterProps) {
178-
const { activeAnimationProgress, containerRef, touchPosition } =
179-
useCommonValuesContext();
168+
const { activeAnimationProgress, touchPosition } = useCommonValuesContext();
180169

181170
const scrollAxis = isVertical ? 'y' : 'x';
182171
const activationOffset = toPair(autoScrollActivationOffset);
@@ -207,41 +196,46 @@ function AutoScrollUpdater({
207196
const scrollBy = useCallback(
208197
(distance: number, animated: boolean) => {
209198
'worklet';
210-
// const ctx = context.value;
211-
// const bounds = contentAxisBounds.value;
212-
// const containerMeasurements = measure(containerRef);
213-
// const scrollableMeasurements = measure(scrollableRef);
214-
// if (
215-
// !ctx ||
216-
// !bounds ||
217-
// !scrollableMeasurements ||
218-
// !containerMeasurements
219-
// ) {
220-
// return;
221-
// }
222-
223-
// const newContainerOffset = ctx.targetContainerOffset + distance;
224-
225-
// if (distance < 0) {
226-
// // Scroll up
227-
// } else if (distance > 0) {
228-
// // Scroll down
229-
// }
230-
231-
// console.log(ctx.targetContainerOffset, distance, newContainerOffset);
232-
233-
// ctx.targetContainerOffset = newContainerOffset;
234-
// const offsetDiff = newContainerOffset - ctx.startContainerOffset;
235-
236-
// scrollToOffset(ctx.startScrollOffset + offsetDiff, animated);
199+
const ctx = context.value;
200+
const bounds = contentAxisBounds.value;
201+
const scrollableMeasurements = measure(scrollableRef);
202+
if (!ctx || !bounds || !scrollableMeasurements) {
203+
return;
204+
}
205+
206+
const scrollableSize =
207+
scrollableMeasurements[isVertical ? 'height' : 'width'];
208+
209+
let newScrollOffset = 0;
210+
211+
if (distance > 0) {
212+
// scroll down
213+
newScrollOffset = Math.min(
214+
ctx.targetScrollOffset + distance,
215+
ctx.sortableOffset - scrollableSize + bounds[1] + maxOverscroll[1]
216+
);
217+
} else if (distance < 0) {
218+
// scroll up
219+
newScrollOffset = Math.max(
220+
ctx.targetScrollOffset + distance,
221+
ctx.sortableOffset + bounds[0] - maxOverscroll[0]
222+
);
223+
}
224+
225+
if (Math.abs(newScrollOffset - ctx.targetScrollOffset) < 1) {
226+
return;
227+
}
228+
229+
ctx.targetScrollOffset = newScrollOffset;
230+
scrollToOffset(newScrollOffset, animated);
237231
},
238232
[
239233
context,
240-
scrollableRef,
241-
containerRef,
234+
scrollToOffset,
242235
contentAxisBounds,
243236
maxOverscroll,
244-
scrollToOffset
237+
isVertical,
238+
scrollableRef
245239
]
246240
);
247241

@@ -346,22 +340,22 @@ function AutoScrollUpdater({
346340
return;
347341
}
348342

349-
const containerPos = scrollOffset.value + ctx.sortableOffset;
343+
const scrollableSize =
344+
scrollableMeasurements[isVertical ? 'height' : 'width'];
345+
const containerPos = ctx.sortableOffset - scrollOffset.value;
346+
350347
ctx.progress = calculateRawProgress(
351348
position,
352349
containerPos,
353-
scrollableMeasurements[isVertical ? 'pageY' : 'pageX'],
354-
scrollableMeasurements[isVertical ? 'height' : 'width'],
350+
scrollableSize,
355351
activationOffset,
356352
bounds,
357353
maxOverscroll,
358354
autoScrollExtrapolation
359355
);
360356

361-
// console.log(ctx.progress);
362-
363357
if (debug) {
364-
debug?.updateDebugRects?.(containerPos, scrollableMeasurements);
358+
debug?.updateDebugRects?.(containerPos, scrollableSize);
365359
}
366360
},
367361
[debug]

packages/react-native-sortables/src/providers/shared/AutoScrollProvider/useDebugHelpers.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback } from 'react';
2-
import type { MeasuredDimensions, SharedValue } from 'react-native-reanimated';
2+
import type { SharedValue } from 'react-native-reanimated';
33

44
import { useDebugContext } from '../../../debug';
55

@@ -37,10 +37,8 @@ export default function useDebugHelpers(
3737
}, [debugRects]);
3838

3939
const updateDebugRects = useCallback(
40-
(containerPos: number, scrollableMeasurements: MeasuredDimensions) => {
40+
(containerPos: number, scrollableSize: number) => {
4141
'worklet';
42-
const { height: sH, width: sW } = scrollableMeasurements;
43-
4442
const startTriggerProps = isVertical
4543
? {
4644
height: startActivationOffset,
@@ -55,12 +53,12 @@ export default function useDebugHelpers(
5553
? {
5654
height: endActivationOffset,
5755
positionOrigin: 'bottom' as const,
58-
y: -containerPos + sH
56+
y: -containerPos + scrollableSize
5957
}
6058
: {
6159
positionOrigin: 'right' as const,
6260
width: endActivationOffset,
63-
x: -containerPos + sW
61+
x: -containerPos + scrollableSize
6462
};
6563

6664
debugRects?.start.set({ ...TRIGGER_COLORS, ...startTriggerProps });

packages/react-native-sortables/src/providers/shared/AutoScrollProvider/utils.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { Extrapolation, interpolate } from 'react-native-reanimated';
55
export const calculateRawProgress = (
66
position: number,
77
containerPos: number,
8-
scrollablePos: number,
98
scrollableSize: number,
109
activationOffset: [number, number],
1110
contentBounds: [number, number],
@@ -20,22 +19,20 @@ export const calculateRawProgress = (
2019
Extrapolation.CLAMP
2120
);
2221

23-
const contentEndPos = containerPos + contentBounds[1];
24-
const endDistance = scrollablePos + scrollableSize - contentEndPos;
22+
const contentSize = contentBounds[1] - contentBounds[0];
23+
const endDistance = scrollableSize - contentSize - startDistance;
2524
const endBoundProgress = interpolate(
2625
endDistance,
2726
[-activationOffset[1], maxOverscroll[1]],
2827
[1, 0],
2928
Extrapolation.CLAMP
3029
);
3130

32-
const startBound = scrollablePos - containerPos;
31+
const startBound = -containerPos;
3332
const startThreshold = startBound + activationOffset[0];
3433
const endBound = startBound + scrollableSize;
3534
const endThreshold = endBound - activationOffset[1];
3635

37-
console.log(position, [startBound, startThreshold, endThreshold, endBound]);
38-
3936
return interpolate(
4037
position,
4138
[startBound, startThreshold, endThreshold, endBound],

packages/react-native-sortables/src/types/providers/shared.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ export type AutoScrollContextType = {
116116
scrollOffsetDiff: SharedValue<number>;
117117
isVerticalScroll: boolean;
118118
contentBounds: SharedValue<[Vector, Vector] | null>;
119-
onContainerLayout: () => void;
120119
scrollBy: (distance: number, animated: boolean) => void;
121120
};
122121

0 commit comments

Comments
 (0)