react-native-reanimated-carousel 2.1.2 → 2.2.4
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 +10 -7
- package/README.zh-CN.md +11 -5
- package/lib/commonjs/Carousel.js +29 -21
- package/lib/commonjs/Carousel.js.map +1 -1
- package/lib/commonjs/ScrollViewGesture.js +29 -21
- package/lib/commonjs/ScrollViewGesture.js.map +1 -1
- package/lib/commonjs/hooks/useAutoPlay.js +27 -13
- package/lib/commonjs/hooks/useAutoPlay.js.map +1 -1
- package/lib/commonjs/hooks/useCarouselController.js +84 -24
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
- package/lib/commonjs/hooks/useInitProps.js +13 -9
- package/lib/commonjs/hooks/useInitProps.js.map +1 -1
- package/lib/commonjs/hooks/useOnProgressChange.js +5 -5
- package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
- package/lib/commonjs/layouts/stack.js.map +1 -1
- package/lib/module/Carousel.js +26 -21
- package/lib/module/Carousel.js.map +1 -1
- package/lib/module/ScrollViewGesture.js +27 -21
- package/lib/module/ScrollViewGesture.js.map +1 -1
- package/lib/module/hooks/useAutoPlay.js +27 -13
- package/lib/module/hooks/useAutoPlay.js.map +1 -1
- package/lib/module/hooks/useCarouselController.js +82 -24
- package/lib/module/hooks/useCarouselController.js.map +1 -1
- package/lib/module/hooks/useInitProps.js +13 -9
- package/lib/module/hooks/useInitProps.js.map +1 -1
- package/lib/module/hooks/useOnProgressChange.js +5 -5
- package/lib/module/hooks/useOnProgressChange.js.map +1 -1
- package/lib/module/layouts/stack.js.map +1 -1
- package/lib/typescript/Carousel.d.ts +3 -4
- package/lib/typescript/ScrollViewGesture.d.ts +3 -1
- package/lib/typescript/hooks/useAutoPlay.d.ts +1 -1
- package/lib/typescript/hooks/useCarouselController.d.ts +5 -3
- package/lib/typescript/hooks/useInitProps.d.ts +4 -5
- package/lib/typescript/hooks/useOnProgressChange.d.ts +2 -1
- package/lib/typescript/layouts/stack.d.ts +1 -1
- package/lib/typescript/types.d.ts +22 -4
- package/package.json +5 -2
- package/src/Carousel.tsx +203 -185
- package/src/ScrollViewGesture.tsx +52 -25
- package/src/hooks/useAutoPlay.ts +25 -22
- package/src/hooks/useCarouselController.tsx +101 -42
- package/src/hooks/useInitProps.ts +30 -15
- package/src/hooks/useOnProgressChange.ts +7 -6
- package/src/layouts/stack.ts +1 -1
- package/src/types.ts +23 -4
- package/CHANGELOG.md +0 -361
- package/lib/commonjs/hooks/useIndexController.js +0 -65
- package/lib/commonjs/hooks/useIndexController.js.map +0 -1
- package/lib/module/hooks/useIndexController.js +0 -52
- package/lib/module/hooks/useIndexController.js.map +0 -1
- package/lib/typescript/hooks/useIndexController.d.ts +0 -18
- package/src/.DS_Store +0 -0
- package/src/hooks/useIndexController.ts +0 -78
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type Animated from 'react-native-reanimated';
|
|
3
3
|
import { Easing } from '../constants';
|
|
4
4
|
import { runOnJS, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
5
|
+
import type { TCarouselActionOptions } from '../types';
|
|
5
6
|
|
|
6
7
|
interface IOpts {
|
|
7
8
|
loop: boolean;
|
|
@@ -22,14 +23,15 @@ export interface ICarouselController {
|
|
|
22
23
|
index: Animated.SharedValue<number>;
|
|
23
24
|
sharedIndex: React.MutableRefObject<number>;
|
|
24
25
|
sharedPreIndex: React.MutableRefObject<number>;
|
|
25
|
-
prev: () => void;
|
|
26
|
-
next: () => void;
|
|
26
|
+
prev: (opts?: TCarouselActionOptions) => void;
|
|
27
|
+
next: (opts?: TCarouselActionOptions) => void;
|
|
27
28
|
computedIndex: () => void;
|
|
28
29
|
getCurrentIndex: () => number;
|
|
29
30
|
to: (index: number, animated?: boolean) => void;
|
|
31
|
+
scrollTo: (opts?: TCarouselActionOptions) => void;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
export function useCarouselController(
|
|
34
|
+
export function useCarouselController(options: IOpts): ICarouselController {
|
|
33
35
|
const {
|
|
34
36
|
size,
|
|
35
37
|
loop,
|
|
@@ -39,13 +41,26 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
39
41
|
length,
|
|
40
42
|
onChange,
|
|
41
43
|
duration,
|
|
42
|
-
} =
|
|
44
|
+
} = options;
|
|
43
45
|
|
|
44
46
|
const index = useSharedValue<number>(0);
|
|
45
47
|
// The Index displayed to the user
|
|
46
48
|
const sharedIndex = React.useRef<number>(0);
|
|
47
49
|
const sharedPreIndex = React.useRef<number>(0);
|
|
48
50
|
|
|
51
|
+
const currentFixedPage = React.useCallback(() => {
|
|
52
|
+
if (loop) {
|
|
53
|
+
return -Math.round(handlerOffsetX.value / size);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const fixed = (handlerOffsetX.value / size) % length;
|
|
57
|
+
return Math.round(
|
|
58
|
+
handlerOffsetX.value <= 0
|
|
59
|
+
? Math.abs(fixed)
|
|
60
|
+
: Math.abs(fixed > 0 ? length - fixed : 0)
|
|
61
|
+
);
|
|
62
|
+
}, [handlerOffsetX, length, size, loop]);
|
|
63
|
+
|
|
49
64
|
const convertToSharedIndex = React.useCallback(
|
|
50
65
|
(i: number) => {
|
|
51
66
|
if (loop) {
|
|
@@ -92,22 +107,22 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
92
107
|
}, [disable]);
|
|
93
108
|
|
|
94
109
|
const onScrollEnd = React.useCallback(() => {
|
|
95
|
-
|
|
96
|
-
}, [
|
|
110
|
+
options.onScrollEnd?.();
|
|
111
|
+
}, [options]);
|
|
97
112
|
|
|
98
113
|
const onScrollBegin = React.useCallback(() => {
|
|
99
|
-
|
|
100
|
-
}, [
|
|
114
|
+
options.onScrollBegin?.();
|
|
115
|
+
}, [options]);
|
|
101
116
|
|
|
102
117
|
const scrollWithTiming = React.useCallback(
|
|
103
|
-
(toValue: number,
|
|
118
|
+
(toValue: number, onFinished?: () => void) => {
|
|
104
119
|
return withTiming(
|
|
105
120
|
toValue,
|
|
106
121
|
{ duration, easing: Easing.easeOutQuart },
|
|
107
122
|
(isFinished: boolean) => {
|
|
108
|
-
callback?.();
|
|
109
123
|
if (isFinished) {
|
|
110
124
|
runOnJS(onScrollEnd)();
|
|
125
|
+
onFinished && runOnJS(onFinished)();
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
128
|
);
|
|
@@ -115,42 +130,70 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
115
130
|
[onScrollEnd, duration]
|
|
116
131
|
);
|
|
117
132
|
|
|
118
|
-
const next = React.useCallback(
|
|
119
|
-
|
|
133
|
+
const next = React.useCallback(
|
|
134
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
135
|
+
const { count = 1, animated = true, onFinished } = opts;
|
|
136
|
+
if (!canSliding() || (!loop && index.value >= length - 1)) return;
|
|
120
137
|
|
|
121
|
-
|
|
138
|
+
onScrollBegin?.();
|
|
122
139
|
|
|
123
|
-
|
|
140
|
+
const nextPage = currentFixedPage() + count;
|
|
141
|
+
index.value = nextPage;
|
|
124
142
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
if (animated) {
|
|
144
|
+
handlerOffsetX.value = scrollWithTiming(
|
|
145
|
+
-nextPage * size,
|
|
146
|
+
onFinished
|
|
147
|
+
);
|
|
148
|
+
} else {
|
|
149
|
+
handlerOffsetX.value = -nextPage * size;
|
|
150
|
+
onFinished?.();
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
[
|
|
154
|
+
canSliding,
|
|
155
|
+
loop,
|
|
156
|
+
index,
|
|
157
|
+
length,
|
|
158
|
+
onScrollBegin,
|
|
159
|
+
handlerOffsetX,
|
|
160
|
+
size,
|
|
161
|
+
scrollWithTiming,
|
|
162
|
+
currentFixedPage,
|
|
163
|
+
]
|
|
164
|
+
);
|
|
136
165
|
|
|
137
|
-
const prev = React.useCallback(
|
|
138
|
-
|
|
166
|
+
const prev = React.useCallback(
|
|
167
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
168
|
+
const { count = 1, animated = true, onFinished } = opts;
|
|
169
|
+
if (!canSliding() || (!loop && index.value <= 0)) return;
|
|
139
170
|
|
|
140
|
-
|
|
171
|
+
onScrollBegin?.();
|
|
141
172
|
|
|
142
|
-
|
|
173
|
+
const prevPage = currentFixedPage() - count;
|
|
174
|
+
index.value = prevPage;
|
|
143
175
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
176
|
+
if (animated) {
|
|
177
|
+
handlerOffsetX.value = scrollWithTiming(
|
|
178
|
+
-prevPage * size,
|
|
179
|
+
onFinished
|
|
180
|
+
);
|
|
181
|
+
} else {
|
|
182
|
+
handlerOffsetX.value = -prevPage * size;
|
|
183
|
+
onFinished?.();
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
[
|
|
187
|
+
canSliding,
|
|
188
|
+
loop,
|
|
189
|
+
index,
|
|
190
|
+
onScrollBegin,
|
|
191
|
+
handlerOffsetX,
|
|
192
|
+
size,
|
|
193
|
+
scrollWithTiming,
|
|
194
|
+
currentFixedPage,
|
|
195
|
+
]
|
|
196
|
+
);
|
|
154
197
|
|
|
155
198
|
const to = React.useCallback(
|
|
156
199
|
(idx: number, animated: boolean = false) => {
|
|
@@ -162,9 +205,8 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
162
205
|
const offset = handlerOffsetX.value + (index.value - idx) * size;
|
|
163
206
|
|
|
164
207
|
if (animated) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
});
|
|
208
|
+
index.value = idx;
|
|
209
|
+
handlerOffsetX.value = scrollWithTiming(offset);
|
|
168
210
|
} else {
|
|
169
211
|
handlerOffsetX.value = offset;
|
|
170
212
|
index.value = idx;
|
|
@@ -182,10 +224,27 @@ export function useCarouselController(opts: IOpts): ICarouselController {
|
|
|
182
224
|
]
|
|
183
225
|
);
|
|
184
226
|
|
|
227
|
+
const scrollTo = React.useCallback(
|
|
228
|
+
(opts: TCarouselActionOptions = {}) => {
|
|
229
|
+
const { count, animated = false, onFinished } = opts;
|
|
230
|
+
if (!count) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const n = Math.round(count);
|
|
234
|
+
if (n < 0) {
|
|
235
|
+
prev({ count: Math.abs(n), animated, onFinished });
|
|
236
|
+
} else {
|
|
237
|
+
next({ count: n, animated, onFinished });
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
[prev, next]
|
|
241
|
+
);
|
|
242
|
+
|
|
185
243
|
return {
|
|
186
244
|
next,
|
|
187
245
|
prev,
|
|
188
246
|
to,
|
|
247
|
+
scrollTo,
|
|
189
248
|
index,
|
|
190
249
|
length,
|
|
191
250
|
sharedIndex,
|
|
@@ -2,21 +2,33 @@ import React from 'react';
|
|
|
2
2
|
import { DATA_LENGTH } from '../constants';
|
|
3
3
|
import type { TCarouselProps } from '../types';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
type TGetRequiredProps<P extends keyof TCarouselProps> = Record<
|
|
6
|
+
P,
|
|
7
|
+
Required<TCarouselProps>[P]
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
export type TInitializeCarouselProps<T> = TCarouselProps<T> &
|
|
11
|
+
TGetRequiredProps<
|
|
12
|
+
| 'defaultIndex'
|
|
13
|
+
| 'loop'
|
|
14
|
+
| 'width'
|
|
15
|
+
| 'height'
|
|
16
|
+
| 'scrollAnimationDuration'
|
|
17
|
+
| 'autoPlayInterval'
|
|
18
|
+
> & {
|
|
19
|
+
// Raw data that has not been processed
|
|
20
|
+
rawData: T[];
|
|
21
|
+
};
|
|
11
22
|
|
|
12
23
|
export function useInitProps<T>(
|
|
13
24
|
props: TCarouselProps<T>
|
|
14
25
|
): TInitializeCarouselProps<T> {
|
|
15
26
|
const {
|
|
16
27
|
defaultIndex = 0,
|
|
17
|
-
data:
|
|
28
|
+
data: rawData = [],
|
|
18
29
|
loop = true,
|
|
19
|
-
autoPlayInterval = 1000,
|
|
30
|
+
autoPlayInterval: _autoPlayInterval = 1000,
|
|
31
|
+
scrollAnimationDuration = 500,
|
|
20
32
|
style = {},
|
|
21
33
|
panGestureHandlerProps = {},
|
|
22
34
|
pagingEnabled = true,
|
|
@@ -27,20 +39,21 @@ export function useInitProps<T>(
|
|
|
27
39
|
|
|
28
40
|
const width = Math.round(_width || 0);
|
|
29
41
|
const height = Math.round(_height || 0);
|
|
42
|
+
const autoPlayInterval = Math.max(_autoPlayInterval, 0);
|
|
30
43
|
|
|
31
44
|
const data = React.useMemo<T[]>(() => {
|
|
32
|
-
if (!loop) return
|
|
45
|
+
if (!loop) return rawData;
|
|
33
46
|
|
|
34
|
-
if (
|
|
35
|
-
return [
|
|
47
|
+
if (rawData.length === DATA_LENGTH.SINGLE_ITEM) {
|
|
48
|
+
return [rawData[0], rawData[0], rawData[0]];
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
if (
|
|
39
|
-
return [
|
|
51
|
+
if (rawData.length === DATA_LENGTH.DOUBLE_ITEM) {
|
|
52
|
+
return [rawData[0], rawData[1], rawData[0], rawData[1]];
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
return
|
|
43
|
-
}, [
|
|
55
|
+
return rawData;
|
|
56
|
+
}, [rawData, loop]);
|
|
44
57
|
|
|
45
58
|
if (props.mode === 'vertical-stack' || props.mode === 'horizontal-stack') {
|
|
46
59
|
if (!props.modeConfig) {
|
|
@@ -53,8 +66,10 @@ export function useInitProps<T>(
|
|
|
53
66
|
...props,
|
|
54
67
|
defaultIndex,
|
|
55
68
|
data,
|
|
69
|
+
rawData,
|
|
56
70
|
loop,
|
|
57
71
|
autoPlayInterval,
|
|
72
|
+
scrollAnimationDuration,
|
|
58
73
|
style,
|
|
59
74
|
panGestureHandlerProps,
|
|
60
75
|
pagingEnabled,
|
|
@@ -9,31 +9,32 @@ export function useOnProgressChange(
|
|
|
9
9
|
opts: {
|
|
10
10
|
size: number;
|
|
11
11
|
offsetX: Animated.SharedValue<number>;
|
|
12
|
-
|
|
12
|
+
rawData: TCarouselProps['data'];
|
|
13
|
+
} & Pick<TCarouselProps, 'onProgressChange'>
|
|
13
14
|
) {
|
|
14
|
-
const { offsetX,
|
|
15
|
+
const { offsetX, rawData, size, onProgressChange } = opts;
|
|
15
16
|
useAnimatedReaction(
|
|
16
17
|
() => offsetX.value,
|
|
17
18
|
(_value) => {
|
|
18
19
|
let value = _value;
|
|
19
20
|
|
|
20
|
-
if (
|
|
21
|
+
if (rawData.length === DATA_LENGTH.SINGLE_ITEM) {
|
|
21
22
|
value = value % size;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
if (
|
|
25
|
+
if (rawData.length === DATA_LENGTH.DOUBLE_ITEM) {
|
|
25
26
|
value = value % (size * 2);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
let absoluteProgress = Math.abs(value / size);
|
|
29
30
|
|
|
30
31
|
if (value > 0) {
|
|
31
|
-
absoluteProgress =
|
|
32
|
+
absoluteProgress = rawData.length - absoluteProgress;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
!!onProgressChange &&
|
|
35
36
|
runOnJS(onProgressChange)(value, absoluteProgress);
|
|
36
37
|
},
|
|
37
|
-
[onProgressChange,
|
|
38
|
+
[onProgressChange, rawData]
|
|
38
39
|
);
|
|
39
40
|
}
|
package/src/layouts/stack.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { Dimensions, TransformsStyle, ViewStyle } from 'react-native';
|
|
3
3
|
import { Extrapolate, interpolate } from 'react-native-reanimated';
|
|
4
|
-
import type { ComputedDirectionTypes, CustomConfig } from '
|
|
4
|
+
import type { ComputedDirectionTypes, CustomConfig } from '../types';
|
|
5
5
|
|
|
6
6
|
const screen = Dimensions.get('window');
|
|
7
7
|
|
package/src/types.ts
CHANGED
|
@@ -71,6 +71,11 @@ export type TCarouselProps<T = any> = {
|
|
|
71
71
|
* @description playback interval
|
|
72
72
|
*/
|
|
73
73
|
autoPlayInterval?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Time a scroll animation takes to finish
|
|
76
|
+
* @default 500 (ms)
|
|
77
|
+
*/
|
|
78
|
+
scrollAnimationDuration?: number;
|
|
74
79
|
/**
|
|
75
80
|
* Carousel container style
|
|
76
81
|
*/
|
|
@@ -138,21 +143,29 @@ export type TCarouselProps<T = any> = {
|
|
|
138
143
|
|
|
139
144
|
export interface ICarouselInstance {
|
|
140
145
|
/**
|
|
141
|
-
*
|
|
146
|
+
* Scroll to previous item, it takes one optional argument (count),
|
|
147
|
+
* which allows you to specify how many items to cross
|
|
142
148
|
*/
|
|
143
|
-
prev: () => void;
|
|
149
|
+
prev: (opts?: TCarouselActionOptions) => void;
|
|
144
150
|
/**
|
|
145
|
-
*
|
|
151
|
+
* Scroll to next item, it takes one optional argument (count),
|
|
152
|
+
* which allows you to specify how many items to cross
|
|
146
153
|
*/
|
|
147
|
-
next: () => void;
|
|
154
|
+
next: (opts?: TCarouselActionOptions) => void;
|
|
148
155
|
/**
|
|
149
156
|
* Get current item index
|
|
150
157
|
*/
|
|
151
158
|
getCurrentIndex: () => number;
|
|
152
159
|
/**
|
|
153
160
|
* Go to index
|
|
161
|
+
* @deprecated use scrollTo instead
|
|
154
162
|
*/
|
|
155
163
|
goToIndex: (index: number, animated?: boolean) => void;
|
|
164
|
+
/**
|
|
165
|
+
* Use value to scroll to a position where relative to the current position,
|
|
166
|
+
* scrollTo(-2) is equivalent to prev(2), scrollTo(2) is equivalent to next(2)
|
|
167
|
+
*/
|
|
168
|
+
scrollTo: (opts?: TCarouselActionOptions) => void;
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
export interface CarouselRenderItemInfo<ItemT> {
|
|
@@ -164,3 +177,9 @@ export interface CarouselRenderItemInfo<ItemT> {
|
|
|
164
177
|
export type CarouselRenderItem<ItemT> = (
|
|
165
178
|
info: CarouselRenderItemInfo<ItemT>
|
|
166
179
|
) => React.ReactElement;
|
|
180
|
+
|
|
181
|
+
export interface TCarouselActionOptions {
|
|
182
|
+
count?: number;
|
|
183
|
+
animated?: boolean;
|
|
184
|
+
onFinished?: () => void;
|
|
185
|
+
}
|