react-native-reanimated-carousel 4.0.0-alpha.7 → 4.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/lib/commonjs/components/BaseLayout.js +2 -20
- package/lib/commonjs/components/BaseLayout.js.map +1 -1
- package/lib/commonjs/components/Carousel.js +21 -45
- package/lib/commonjs/components/Carousel.js.map +1 -1
- package/lib/commonjs/components/ItemRenderer.js +80 -0
- package/lib/commonjs/components/ItemRenderer.js.map +1 -0
- package/lib/commonjs/components/ScrollViewGesture.js +5 -5
- package/lib/commonjs/components/ScrollViewGesture.js.map +1 -1
- package/lib/commonjs/hooks/useCarouselController.js +9 -9
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
- package/lib/commonjs/hooks/useOffsetX.test.js.map +1 -1
- package/lib/commonjs/hooks/usePanGestureProxy.js +12 -12
- package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -1
- package/lib/commonjs/hooks/usePanGestureProxy.test.js +8 -8
- package/lib/commonjs/hooks/usePanGestureProxy.test.js.map +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js +33 -16
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/BaseLayout.js +3 -15
- package/lib/module/components/BaseLayout.js.map +1 -1
- package/lib/module/components/Carousel.js +21 -42
- package/lib/module/components/Carousel.js.map +1 -1
- package/lib/module/components/ItemRenderer.js +62 -0
- package/lib/module/components/ItemRenderer.js.map +1 -0
- package/lib/module/components/ScrollViewGesture.js +5 -5
- package/lib/module/components/ScrollViewGesture.js.map +1 -1
- package/lib/module/hooks/useCarouselController.js +9 -9
- package/lib/module/hooks/useCarouselController.js.map +1 -1
- package/lib/module/hooks/useOffsetX.test.js.map +1 -1
- package/lib/module/hooks/usePanGestureProxy.js +12 -12
- package/lib/module/hooks/usePanGestureProxy.js.map +1 -1
- package/lib/module/hooks/usePanGestureProxy.test.js +8 -8
- package/lib/module/hooks/usePanGestureProxy.test.js.map +1 -1
- package/lib/module/hooks/useVisibleRanges.js +32 -16
- package/lib/module/hooks/useVisibleRanges.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/ItemRenderer.d.ts +22 -0
- package/lib/typescript/components/ScrollViewGesture.d.ts +1 -1
- package/lib/typescript/hooks/useCarouselController.d.ts +1 -1
- package/lib/typescript/hooks/usePanGestureProxy.d.ts +1 -1
- package/lib/typescript/hooks/useVisibleRanges.d.ts +7 -4
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/components/BaseLayout.tsx +3 -33
- package/src/components/Carousel.tsx +21 -58
- package/src/components/ItemRenderer.tsx +105 -0
- package/src/components/ScrollViewGesture.tsx +6 -6
- package/src/hooks/useCarouselController.tsx +9 -9
- package/src/hooks/useOffsetX.test.ts +1 -1
- package/src/hooks/usePanGestureProxy.test.tsx +6 -7
- package/src/hooks/usePanGestureProxy.ts +15 -15
- package/src/hooks/useVisibleRanges.tsx +54 -22
- package/src/index.tsx +2 -0
- package/src/types.ts +2 -2
|
@@ -3,7 +3,7 @@ import { StyleSheet } from "react-native";
|
|
|
3
3
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
4
4
|
import { runOnJS, useDerivedValue } from "react-native-reanimated";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ItemRenderer } from "./ItemRenderer";
|
|
7
7
|
import { ScrollViewGesture } from "./ScrollViewGesture";
|
|
8
8
|
|
|
9
9
|
import { useAutoPlay } from "../hooks/useAutoPlay";
|
|
@@ -13,7 +13,6 @@ import { useInitProps } from "../hooks/useInitProps";
|
|
|
13
13
|
import { useLayoutConfig } from "../hooks/useLayoutConfig";
|
|
14
14
|
import { useOnProgressChange } from "../hooks/useOnProgressChange";
|
|
15
15
|
import { usePropsErrorBoundary } from "../hooks/usePropsErrorBoundary";
|
|
16
|
-
import { useVisibleRanges } from "../hooks/useVisibleRanges";
|
|
17
16
|
import { CTX } from "../store";
|
|
18
17
|
import type { ICarouselInstance, TCarouselProps } from "../types";
|
|
19
18
|
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
@@ -30,8 +29,6 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
30
29
|
data,
|
|
31
30
|
// Length of fill data
|
|
32
31
|
dataLength,
|
|
33
|
-
// Raw data that has not been processed
|
|
34
|
-
rawData,
|
|
35
32
|
// Length of raw data
|
|
36
33
|
rawDataLength,
|
|
37
34
|
mode,
|
|
@@ -49,7 +46,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
49
46
|
renderItem,
|
|
50
47
|
onScrollEnd,
|
|
51
48
|
onSnapToItem,
|
|
52
|
-
|
|
49
|
+
onScrollStart,
|
|
53
50
|
onProgressChange,
|
|
54
51
|
customAnimation,
|
|
55
52
|
defaultIndex,
|
|
@@ -89,7 +86,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
89
86
|
fixedDirection,
|
|
90
87
|
duration: scrollAnimationDuration,
|
|
91
88
|
onScrollEnd: () => runOnJS(_onScrollEnd)(),
|
|
92
|
-
|
|
89
|
+
onScrollStart: () => !!onScrollStart && runOnJS(onScrollStart)(),
|
|
93
90
|
});
|
|
94
91
|
|
|
95
92
|
const { next, prev, scrollTo, getSharedIndex, getCurrentIndex }
|
|
@@ -126,10 +123,10 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
126
123
|
onScrollEnd,
|
|
127
124
|
]);
|
|
128
125
|
|
|
129
|
-
const
|
|
126
|
+
const scrollViewGestureOnScrollStart = React.useCallback(() => {
|
|
130
127
|
pauseAutoPlay();
|
|
131
|
-
|
|
132
|
-
}, [
|
|
128
|
+
onScrollStart?.();
|
|
129
|
+
}, [onScrollStart, pauseAutoPlay]);
|
|
133
130
|
|
|
134
131
|
const scrollViewGestureOnScrollEnd = React.useCallback(() => {
|
|
135
132
|
startAutoPlay();
|
|
@@ -155,55 +152,8 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
155
152
|
[getCurrentIndex, next, prev, scrollTo],
|
|
156
153
|
);
|
|
157
154
|
|
|
158
|
-
const visibleRanges = useVisibleRanges({
|
|
159
|
-
total: dataLength,
|
|
160
|
-
viewSize: size,
|
|
161
|
-
translation: handlerOffset,
|
|
162
|
-
windowSize,
|
|
163
|
-
loop,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
155
|
const layoutConfig = useLayoutConfig({ ...props, size });
|
|
167
156
|
|
|
168
|
-
const renderLayout = React.useCallback(
|
|
169
|
-
(item: any, i: number) => {
|
|
170
|
-
const realIndex = computedRealIndexWithAutoFillData({
|
|
171
|
-
index: i,
|
|
172
|
-
dataLength: rawDataLength,
|
|
173
|
-
loop,
|
|
174
|
-
autoFillData,
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<BaseLayout
|
|
179
|
-
key={i}
|
|
180
|
-
index={i}
|
|
181
|
-
handlerOffset={offsetX}
|
|
182
|
-
visibleRanges={visibleRanges}
|
|
183
|
-
animationStyle={customAnimation || layoutConfig}
|
|
184
|
-
>
|
|
185
|
-
{({ animationValue }) =>
|
|
186
|
-
renderItem({
|
|
187
|
-
item,
|
|
188
|
-
index: realIndex,
|
|
189
|
-
animationValue,
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
</BaseLayout>
|
|
193
|
-
);
|
|
194
|
-
},
|
|
195
|
-
[
|
|
196
|
-
loop,
|
|
197
|
-
rawData,
|
|
198
|
-
offsetX,
|
|
199
|
-
visibleRanges,
|
|
200
|
-
autoFillData,
|
|
201
|
-
renderItem,
|
|
202
|
-
layoutConfig,
|
|
203
|
-
customAnimation,
|
|
204
|
-
],
|
|
205
|
-
);
|
|
206
|
-
|
|
207
157
|
return (
|
|
208
158
|
<GestureHandlerRootView>
|
|
209
159
|
<CTX.Provider value={{ props, common: commonVariables }}>
|
|
@@ -223,12 +173,25 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
223
173
|
: styles.itemsHorizontal,
|
|
224
174
|
]}
|
|
225
175
|
testID={testID}
|
|
226
|
-
|
|
176
|
+
onScrollStart={scrollViewGestureOnScrollStart}
|
|
227
177
|
onScrollEnd={scrollViewGestureOnScrollEnd}
|
|
228
178
|
onTouchBegin={scrollViewGestureOnTouchBegin}
|
|
229
179
|
onTouchEnd={scrollViewGestureOnTouchEnd}
|
|
230
180
|
>
|
|
231
|
-
|
|
181
|
+
<ItemRenderer
|
|
182
|
+
data={data}
|
|
183
|
+
dataLength={dataLength}
|
|
184
|
+
rawDataLength={rawDataLength}
|
|
185
|
+
loop={loop}
|
|
186
|
+
size={size}
|
|
187
|
+
windowSize={windowSize}
|
|
188
|
+
autoFillData={autoFillData}
|
|
189
|
+
offsetX={offsetX}
|
|
190
|
+
handlerOffset={handlerOffset}
|
|
191
|
+
layoutConfig={layoutConfig}
|
|
192
|
+
renderItem={renderItem}
|
|
193
|
+
customAnimation={customAnimation}
|
|
194
|
+
/>
|
|
232
195
|
</ScrollViewGesture>
|
|
233
196
|
</CTX.Provider>
|
|
234
197
|
</GestureHandlerRootView>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import type { ViewStyle } from "react-native";
|
|
4
|
+
import type Animated from "react-native-reanimated";
|
|
5
|
+
import { useAnimatedReaction, type AnimatedStyleProp, runOnJS } from "react-native-reanimated";
|
|
6
|
+
|
|
7
|
+
import type { TAnimationStyle } from "./BaseLayout";
|
|
8
|
+
import { BaseLayout } from "./BaseLayout";
|
|
9
|
+
|
|
10
|
+
import type { VisibleRanges } from "../hooks/useVisibleRanges";
|
|
11
|
+
import { useVisibleRanges } from "../hooks/useVisibleRanges";
|
|
12
|
+
import type { CarouselRenderItem } from "../types";
|
|
13
|
+
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
data: any[]
|
|
17
|
+
dataLength: number
|
|
18
|
+
rawDataLength: number
|
|
19
|
+
loop: boolean
|
|
20
|
+
size: number
|
|
21
|
+
windowSize?: number
|
|
22
|
+
autoFillData: boolean
|
|
23
|
+
offsetX: Animated.SharedValue<number>
|
|
24
|
+
handlerOffset: Animated.SharedValue<number>
|
|
25
|
+
layoutConfig: TAnimationStyle
|
|
26
|
+
renderItem: CarouselRenderItem<any>
|
|
27
|
+
customAnimation?: ((value: number) => AnimatedStyleProp<ViewStyle>)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const ItemRenderer: FC<Props> = (props) => {
|
|
31
|
+
const {
|
|
32
|
+
data,
|
|
33
|
+
size,
|
|
34
|
+
windowSize,
|
|
35
|
+
handlerOffset,
|
|
36
|
+
offsetX,
|
|
37
|
+
dataLength,
|
|
38
|
+
rawDataLength,
|
|
39
|
+
loop,
|
|
40
|
+
autoFillData,
|
|
41
|
+
layoutConfig,
|
|
42
|
+
renderItem,
|
|
43
|
+
customAnimation,
|
|
44
|
+
} = props;
|
|
45
|
+
|
|
46
|
+
const visibleRanges = useVisibleRanges({
|
|
47
|
+
total: dataLength,
|
|
48
|
+
viewSize: size,
|
|
49
|
+
translation: handlerOffset,
|
|
50
|
+
windowSize,
|
|
51
|
+
loop,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const [displayedItems, setDisplayedItems] = React.useState<VisibleRanges>(null!);
|
|
55
|
+
|
|
56
|
+
useAnimatedReaction(
|
|
57
|
+
() => visibleRanges.value,
|
|
58
|
+
ranges => runOnJS(setDisplayedItems)(ranges),
|
|
59
|
+
[visibleRanges],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!displayedItems)
|
|
63
|
+
return null;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
{
|
|
68
|
+
data.map((item, index) => {
|
|
69
|
+
const realIndex = computedRealIndexWithAutoFillData({
|
|
70
|
+
index,
|
|
71
|
+
dataLength: rawDataLength,
|
|
72
|
+
loop,
|
|
73
|
+
autoFillData,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const { negativeRange, positiveRange } = displayedItems;
|
|
77
|
+
|
|
78
|
+
const shouldRender = (index >= negativeRange[0] && index <= negativeRange[1])
|
|
79
|
+
|| (index >= positiveRange[0] && index <= positiveRange[1]);
|
|
80
|
+
|
|
81
|
+
if (!shouldRender)
|
|
82
|
+
return null;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<BaseLayout
|
|
86
|
+
key={index}
|
|
87
|
+
index={index}
|
|
88
|
+
handlerOffset={offsetX}
|
|
89
|
+
visibleRanges={visibleRanges}
|
|
90
|
+
animationStyle={customAnimation || layoutConfig}
|
|
91
|
+
>
|
|
92
|
+
{({ animationValue }) =>
|
|
93
|
+
renderItem({
|
|
94
|
+
item,
|
|
95
|
+
index: realIndex,
|
|
96
|
+
animationValue,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
</BaseLayout>
|
|
100
|
+
);
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
@@ -25,7 +25,7 @@ interface Props {
|
|
|
25
25
|
infinite?: boolean
|
|
26
26
|
testID?: string
|
|
27
27
|
style?: StyleProp<ViewStyle>
|
|
28
|
-
|
|
28
|
+
onScrollStart?: () => void
|
|
29
29
|
onScrollEnd?: () => void
|
|
30
30
|
onTouchBegin?: () => void
|
|
31
31
|
onTouchEnd?: () => void
|
|
@@ -55,7 +55,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
|
55
55
|
translation,
|
|
56
56
|
testID,
|
|
57
57
|
style = {},
|
|
58
|
-
|
|
58
|
+
onScrollStart,
|
|
59
59
|
onScrollEnd,
|
|
60
60
|
onTouchBegin,
|
|
61
61
|
onTouchEnd,
|
|
@@ -259,12 +259,12 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
|
259
259
|
return translation;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
const
|
|
262
|
+
const onGestureStart = useCallback((_: PanGestureHandlerEventPayload) => {
|
|
263
263
|
"worklet";
|
|
264
264
|
|
|
265
265
|
touching.value = true;
|
|
266
266
|
validStart.value = true;
|
|
267
|
-
|
|
267
|
+
onScrollStart && runOnJS(onScrollStart)();
|
|
268
268
|
|
|
269
269
|
max.value = (maxPage - 1) * size;
|
|
270
270
|
if (!loop && !overscrollEnabled)
|
|
@@ -282,7 +282,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
|
282
282
|
translation,
|
|
283
283
|
overscrollEnabled,
|
|
284
284
|
getLimit,
|
|
285
|
-
|
|
285
|
+
onScrollStart,
|
|
286
286
|
]);
|
|
287
287
|
|
|
288
288
|
const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
|
|
@@ -380,7 +380,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
|
380
380
|
|
|
381
381
|
const gesture = usePanGestureProxy({
|
|
382
382
|
onConfigurePanGesture,
|
|
383
|
-
|
|
383
|
+
onGestureStart,
|
|
384
384
|
onGestureUpdate,
|
|
385
385
|
onGestureEnd,
|
|
386
386
|
options: { enabled },
|
|
@@ -27,7 +27,7 @@ interface IOpts {
|
|
|
27
27
|
fixedDirection?: TCarouselProps["fixedDirection"]
|
|
28
28
|
duration?: number
|
|
29
29
|
defaultIndex?: number
|
|
30
|
-
|
|
30
|
+
onScrollStart?: () => void
|
|
31
31
|
onScrollEnd?: () => void
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -138,8 +138,8 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
138
138
|
options.onScrollEnd?.();
|
|
139
139
|
}, [options]);
|
|
140
140
|
|
|
141
|
-
const
|
|
142
|
-
options.
|
|
141
|
+
const onScrollStart = React.useCallback(() => {
|
|
142
|
+
options.onScrollStart?.();
|
|
143
143
|
}, [options]);
|
|
144
144
|
|
|
145
145
|
const scrollWithTiming = React.useCallback(
|
|
@@ -173,7 +173,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
173
173
|
if (!canSliding() || (!loop && index.value >= dataInfo.length - 1))
|
|
174
174
|
return;
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
onScrollStart?.();
|
|
177
177
|
|
|
178
178
|
const nextPage = currentFixedPage() + count;
|
|
179
179
|
index.value = nextPage;
|
|
@@ -194,7 +194,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
194
194
|
loop,
|
|
195
195
|
index,
|
|
196
196
|
dataInfo,
|
|
197
|
-
|
|
197
|
+
onScrollStart,
|
|
198
198
|
handlerOffset,
|
|
199
199
|
size,
|
|
200
200
|
scrollWithTiming,
|
|
@@ -207,7 +207,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
207
207
|
const { count = 1, animated = true, onFinished } = opts;
|
|
208
208
|
if (!canSliding() || (!loop && index.value <= 0)) return;
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
onScrollStart?.();
|
|
211
211
|
|
|
212
212
|
const prevPage = currentFixedPage() - count;
|
|
213
213
|
index.value = prevPage;
|
|
@@ -227,7 +227,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
227
227
|
canSliding,
|
|
228
228
|
loop,
|
|
229
229
|
index,
|
|
230
|
-
|
|
230
|
+
onScrollStart,
|
|
231
231
|
handlerOffset,
|
|
232
232
|
size,
|
|
233
233
|
scrollWithTiming,
|
|
@@ -241,7 +241,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
241
241
|
if (i === index.value) return;
|
|
242
242
|
if (!canSliding()) return;
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
onScrollStart?.();
|
|
245
245
|
// direction -> 1 | -1
|
|
246
246
|
const direction = handlerOffsetDirection(handlerOffset, fixedDirection);
|
|
247
247
|
|
|
@@ -283,7 +283,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
283
283
|
handlerOffset,
|
|
284
284
|
dataInfo.length,
|
|
285
285
|
canSliding,
|
|
286
|
-
|
|
286
|
+
onScrollStart,
|
|
287
287
|
scrollWithTiming,
|
|
288
288
|
],
|
|
289
289
|
);
|
|
@@ -46,14 +46,13 @@ describe("Using RNGH v2 gesture API", () => {
|
|
|
46
46
|
onConfigurePanGesture: (gesture: PanGesture) => {
|
|
47
47
|
// This is user's customizations
|
|
48
48
|
gesture
|
|
49
|
-
.onStart(treatStartAsUpdate ? handlers.active : handlers.start)
|
|
50
49
|
.onBegin(handlersFromUser.begin)
|
|
51
50
|
.onUpdate(handlersFromUser.active)
|
|
52
51
|
.onEnd(handlersFromUser.end)
|
|
53
52
|
.onFinalize(handlers.finish)
|
|
54
53
|
.withTestId("pan");
|
|
55
54
|
},
|
|
56
|
-
|
|
55
|
+
onGestureStart: treatStartAsUpdate ? handlers.active : handlers.start,
|
|
57
56
|
onGestureUpdate: handlers.active,
|
|
58
57
|
onGestureEnd: handlers.end,
|
|
59
58
|
options: { enabled: true },
|
|
@@ -82,10 +81,11 @@ describe("Using RNGH v2 gesture API", () => {
|
|
|
82
81
|
const pan = usePanGestureProxy({
|
|
83
82
|
onConfigurePanGesture: (_: PanGesture) => {
|
|
84
83
|
_
|
|
84
|
+
.onBegin(panHandlers.begin)
|
|
85
85
|
.onFinalize(panHandlers.finish)
|
|
86
86
|
.withTestId("pan");
|
|
87
87
|
},
|
|
88
|
-
|
|
88
|
+
onGestureStart: panHandlers.start,
|
|
89
89
|
onGestureUpdate: panHandlers.active,
|
|
90
90
|
onGestureEnd: panHandlers.end,
|
|
91
91
|
options: { enabled: true },
|
|
@@ -153,14 +153,13 @@ describe("Event list validation", () => {
|
|
|
153
153
|
const pan = usePanGestureProxy({
|
|
154
154
|
onConfigurePanGesture: (_: PanGesture) => {
|
|
155
155
|
_
|
|
156
|
-
.onStart(treatStartAsUpdate ? handlers.active : handlers.start)
|
|
157
156
|
.onBegin(handlersFromUser.begin)
|
|
158
157
|
.onUpdate(handlersFromUser.active)
|
|
159
158
|
.onEnd(handlersFromUser.end)
|
|
160
159
|
.onFinalize(handlers.finish)
|
|
161
160
|
.withTestId("pan");
|
|
162
161
|
},
|
|
163
|
-
|
|
162
|
+
onGestureStart: treatStartAsUpdate ? handlers.active : handlers.start,
|
|
164
163
|
onGestureUpdate: handlers.active,
|
|
165
164
|
onGestureEnd: handlers.end,
|
|
166
165
|
options: { enabled: true },
|
|
@@ -233,11 +232,11 @@ describe("Filling event list with defaults", () => {
|
|
|
233
232
|
const pan = usePanGestureProxy({
|
|
234
233
|
onConfigurePanGesture: (_: PanGesture) => {
|
|
235
234
|
_
|
|
236
|
-
.
|
|
235
|
+
.onBegin(handlers.begin)
|
|
237
236
|
.onFinalize(handlers.finish)
|
|
238
237
|
.withTestId("pan");
|
|
239
238
|
},
|
|
240
|
-
|
|
239
|
+
onGestureStart: treatStartAsUpdate ? handlers.active : handlers.start,
|
|
241
240
|
onGestureUpdate: handlers.active,
|
|
242
241
|
onGestureEnd: handlers.end,
|
|
243
242
|
options: { enabled: true },
|
|
@@ -8,7 +8,7 @@ import { useUpdateGestureConfig } from "./useUpdateGestureConfig";
|
|
|
8
8
|
export const usePanGestureProxy = (
|
|
9
9
|
customization: {
|
|
10
10
|
onConfigurePanGesture?: (gesture: PanGesture) => void
|
|
11
|
-
|
|
11
|
+
onGestureStart: (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => void
|
|
12
12
|
onGestureUpdate: (event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => void
|
|
13
13
|
onGestureEnd: (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>, success: boolean) => void
|
|
14
14
|
options?: GestureConfig
|
|
@@ -16,7 +16,7 @@ export const usePanGestureProxy = (
|
|
|
16
16
|
) => {
|
|
17
17
|
const {
|
|
18
18
|
onConfigurePanGesture,
|
|
19
|
-
|
|
19
|
+
onGestureStart,
|
|
20
20
|
onGestureUpdate,
|
|
21
21
|
onGestureEnd,
|
|
22
22
|
options = {},
|
|
@@ -27,25 +27,25 @@ export const usePanGestureProxy = (
|
|
|
27
27
|
|
|
28
28
|
// Save the original gesture callbacks
|
|
29
29
|
const originalGestures = {
|
|
30
|
-
|
|
30
|
+
onStart: gesture.onStart,
|
|
31
31
|
onUpdate: gesture.onUpdate,
|
|
32
32
|
onEnd: gesture.onEnd,
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
// Save the user defined gesture callbacks
|
|
36
36
|
const userDefinedConflictGestures: {
|
|
37
|
-
|
|
37
|
+
onStart?: Parameters<(typeof gesture)["onStart"]>[0]
|
|
38
38
|
onUpdate?: Parameters<(typeof gesture)["onUpdate"]>[0]
|
|
39
39
|
onEnd?: Parameters<(typeof gesture)["onEnd"]>[0]
|
|
40
40
|
} = {
|
|
41
|
-
|
|
41
|
+
onStart: undefined,
|
|
42
42
|
onUpdate: undefined,
|
|
43
43
|
onEnd: undefined,
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
// Using
|
|
48
|
-
userDefinedConflictGestures.
|
|
46
|
+
const fakeOnStart: typeof gesture.onStart = (cb) => {
|
|
47
|
+
// Using fakeOnStart to save the user defined callback
|
|
48
|
+
userDefinedConflictGestures.onStart = cb;
|
|
49
49
|
return gesture;
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -62,7 +62,7 @@ export const usePanGestureProxy = (
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
// Setup the fake callbacks
|
|
65
|
-
gesture.
|
|
65
|
+
gesture.onStart = fakeOnStart;
|
|
66
66
|
gesture.onUpdate = fakeOnUpdate;
|
|
67
67
|
gesture.onEnd = fakeOnEnd;
|
|
68
68
|
|
|
@@ -71,17 +71,17 @@ export const usePanGestureProxy = (
|
|
|
71
71
|
onConfigurePanGesture(gesture);
|
|
72
72
|
|
|
73
73
|
// Restore the original callbacks
|
|
74
|
-
gesture.
|
|
74
|
+
gesture.onStart = originalGestures.onStart;
|
|
75
75
|
gesture.onUpdate = originalGestures.onUpdate;
|
|
76
76
|
gesture.onEnd = originalGestures.onEnd;
|
|
77
77
|
|
|
78
78
|
// Setup the original callbacks with the user defined callbacks
|
|
79
79
|
gesture
|
|
80
|
-
.
|
|
81
|
-
|
|
80
|
+
.onStart((e) => {
|
|
81
|
+
onGestureStart(e);
|
|
82
82
|
|
|
83
|
-
if (userDefinedConflictGestures.
|
|
84
|
-
userDefinedConflictGestures.
|
|
83
|
+
if (userDefinedConflictGestures.onStart)
|
|
84
|
+
userDefinedConflictGestures.onStart(e);
|
|
85
85
|
})
|
|
86
86
|
.onUpdate((e) => {
|
|
87
87
|
onGestureUpdate(e);
|
|
@@ -98,7 +98,7 @@ export const usePanGestureProxy = (
|
|
|
98
98
|
|
|
99
99
|
return gesture;
|
|
100
100
|
}, [
|
|
101
|
-
|
|
101
|
+
onGestureStart,
|
|
102
102
|
onGestureUpdate,
|
|
103
103
|
onGestureEnd,
|
|
104
104
|
onConfigurePanGesture,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
1
2
|
import type Animated from "react-native-reanimated";
|
|
2
3
|
import { useDerivedValue } from "react-native-reanimated";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
type Range = [number, number];
|
|
6
|
+
|
|
7
|
+
export interface VisibleRanges {
|
|
8
|
+
negativeRange: Range
|
|
9
|
+
positiveRange: Range
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type IVisibleRanges = Animated.SharedValue<VisibleRanges>;
|
|
8
13
|
|
|
9
14
|
export function useVisibleRanges(options: {
|
|
10
15
|
total: number
|
|
@@ -22,6 +27,7 @@ export function useVisibleRanges(options: {
|
|
|
22
27
|
} = options;
|
|
23
28
|
|
|
24
29
|
const windowSize = _windowSize ?? total;
|
|
30
|
+
const cachedRanges = useRef<VisibleRanges>(null!);
|
|
25
31
|
|
|
26
32
|
const ranges = useDerivedValue(() => {
|
|
27
33
|
const positiveCount = Math.round(windowSize / 2);
|
|
@@ -30,36 +36,62 @@ export function useVisibleRanges(options: {
|
|
|
30
36
|
let currentIndex = Math.round(-translation.value / viewSize);
|
|
31
37
|
currentIndex = currentIndex < 0 ? (currentIndex % total) + total : currentIndex;
|
|
32
38
|
|
|
39
|
+
let newRanges: VisibleRanges;
|
|
40
|
+
|
|
33
41
|
if (!loop) {
|
|
34
42
|
// Adjusting negative range if the carousel is not loopable.
|
|
35
43
|
// So, It will be only displayed the positive items.
|
|
36
|
-
|
|
44
|
+
newRanges = {
|
|
37
45
|
negativeRange: [0 + currentIndex - (windowSize - 1), 0 + currentIndex],
|
|
38
46
|
positiveRange: [0 + currentIndex, currentIndex + (windowSize - 1)],
|
|
39
47
|
};
|
|
40
48
|
}
|
|
49
|
+
else {
|
|
50
|
+
const negativeRange: Range = [
|
|
51
|
+
(currentIndex - negativeCount + total) % total,
|
|
52
|
+
(currentIndex - 1 + total) % total,
|
|
53
|
+
];
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
const positiveRange: Range = [
|
|
56
|
+
(currentIndex + total) % total,
|
|
57
|
+
(currentIndex + positiveCount + total) % total,
|
|
58
|
+
];
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
if (negativeRange[0] < total && negativeRange[0] > negativeRange[1]) {
|
|
61
|
+
negativeRange[1] = total - 1;
|
|
62
|
+
positiveRange[0] = 0;
|
|
63
|
+
}
|
|
64
|
+
if (positiveRange[0] > positiveRange[1]) {
|
|
65
|
+
negativeRange[1] = total - 1;
|
|
66
|
+
positiveRange[0] = 0;
|
|
67
|
+
}
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
positiveRange[0] = 0;
|
|
55
|
-
}
|
|
56
|
-
if (positiveRange[0] > positiveRange[1]) {
|
|
57
|
-
negativeRange[1] = total - 1;
|
|
58
|
-
positiveRange[0] = 0;
|
|
69
|
+
// console.log({ negativeRange, positiveRange ,total,windowSize,a:total <= _windowSize})
|
|
70
|
+
newRanges = { negativeRange, positiveRange };
|
|
59
71
|
}
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
|
|
73
|
+
if (
|
|
74
|
+
isArraysEqual(
|
|
75
|
+
cachedRanges.current?.negativeRange ?? [],
|
|
76
|
+
newRanges.negativeRange,
|
|
77
|
+
)
|
|
78
|
+
&& isArraysEqual(
|
|
79
|
+
cachedRanges.current?.positiveRange ?? [],
|
|
80
|
+
newRanges.positiveRange,
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
return cachedRanges.current;
|
|
84
|
+
|
|
85
|
+
cachedRanges.current = newRanges;
|
|
86
|
+
return cachedRanges.current;
|
|
62
87
|
}, [loop, total, windowSize, translation]);
|
|
63
88
|
|
|
64
89
|
return ranges;
|
|
65
90
|
}
|
|
91
|
+
|
|
92
|
+
function isArraysEqual(a: number[], b: number[]): boolean {
|
|
93
|
+
"worklet";
|
|
94
|
+
if (a.length !== b.length) return false;
|
|
95
|
+
|
|
96
|
+
return a.every((value, index) => value === b[index]);
|
|
97
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import Carousel from "./components/Carousel";
|
|
2
|
+
|
|
2
3
|
export type {
|
|
3
4
|
TCarouselProps,
|
|
4
5
|
ICarouselInstance,
|
|
5
6
|
IComputedDirectionTypes,
|
|
6
7
|
CarouselRenderItem,
|
|
7
8
|
} from "./types";
|
|
9
|
+
export type { TAnimationStyle } from "./components/BaseLayout";
|
|
8
10
|
export type { ILayoutConfig } from "./layouts/stack";
|
|
9
11
|
|
|
10
12
|
export default Carousel;
|
package/src/types.ts
CHANGED
|
@@ -180,9 +180,9 @@ export type TCarouselProps<T = any> = {
|
|
|
180
180
|
*/
|
|
181
181
|
onSnapToItem?: (index: number) => void
|
|
182
182
|
/**
|
|
183
|
-
* On scroll
|
|
183
|
+
* On scroll start
|
|
184
184
|
*/
|
|
185
|
-
|
|
185
|
+
onScrollStart?: () => void
|
|
186
186
|
/**
|
|
187
187
|
* On scroll end
|
|
188
188
|
*/
|