react-native-reanimated-carousel 2.1.2 → 2.2.1
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 +9 -6
- package/README.zh-CN.md +10 -4
- package/lib/commonjs/Carousel.js +9 -7
- package/lib/commonjs/Carousel.js.map +1 -1
- package/lib/commonjs/ScrollViewGesture.js +3 -3
- package/lib/commonjs/ScrollViewGesture.js.map +1 -1
- package/lib/commonjs/hooks/useAutoPlay.js +22 -16
- 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 +4 -1
- package/lib/commonjs/hooks/useInitProps.js.map +1 -1
- package/lib/module/Carousel.js +9 -7
- package/lib/module/Carousel.js.map +1 -1
- package/lib/module/ScrollViewGesture.js +3 -3
- package/lib/module/ScrollViewGesture.js.map +1 -1
- package/lib/module/hooks/useAutoPlay.js +22 -16
- 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 +4 -1
- package/lib/module/hooks/useInitProps.js.map +1 -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 +3 -6
- package/lib/typescript/types.d.ts +22 -4
- package/package.json +5 -2
- package/src/Carousel.tsx +10 -6
- package/src/ScrollViewGesture.tsx +3 -3
- package/src/hooks/useAutoPlay.ts +20 -23
- package/src/hooks/useCarouselController.tsx +101 -42
- package/src/hooks/useInitProps.ts +18 -7
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useInitProps.ts"],"names":["React","DATA_LENGTH","useInitProps","props","defaultIndex","data","_data","loop","autoPlayInterval","style","panGestureHandlerProps","pagingEnabled","enableSnap","width","_width","height","_height","Math","round","useMemo","length","SINGLE_ITEM","DOUBLE_ITEM","mode","modeConfig","showLength"],"mappings":"AAAA,OAAOA,KAAP,MAAkB,OAAlB;AACA,SAASC,WAAT,QAA4B,cAA5B;
|
|
1
|
+
{"version":3,"sources":["useInitProps.ts"],"names":["React","DATA_LENGTH","useInitProps","props","defaultIndex","data","_data","loop","autoPlayInterval","_autoPlayInterval","scrollAnimationDuration","style","panGestureHandlerProps","pagingEnabled","enableSnap","width","_width","height","_height","Math","round","max","useMemo","length","SINGLE_ITEM","DOUBLE_ITEM","mode","modeConfig","showLength"],"mappings":"AAAA,OAAOA,KAAP,MAAkB,OAAlB;AACA,SAASC,WAAT,QAA4B,cAA5B;AAkBA,OAAO,SAASC,YAAT,CACHC,KADG,EAEwB;AAC3B,QAAM;AACFC,IAAAA,YAAY,GAAG,CADb;AAEFC,IAAAA,IAAI,EAAEC,KAAK,GAAG,EAFZ;AAGFC,IAAAA,IAAI,GAAG,IAHL;AAIFC,IAAAA,gBAAgB,EAAEC,iBAAiB,GAAG,IAJpC;AAKFC,IAAAA,uBAAuB,GAAG,GALxB;AAMFC,IAAAA,KAAK,GAAG,EANN;AAOFC,IAAAA,sBAAsB,GAAG,EAPvB;AAQFC,IAAAA,aAAa,GAAG,IARd;AASFC,IAAAA,UAAU,GAAG,IATX;AAUFC,IAAAA,KAAK,EAAEC,MAVL;AAWFC,IAAAA,MAAM,EAAEC;AAXN,MAYFf,KAZJ;AAcA,QAAMY,KAAK,GAAGI,IAAI,CAACC,KAAL,CAAWJ,MAAM,IAAI,CAArB,CAAd;AACA,QAAMC,MAAM,GAAGE,IAAI,CAACC,KAAL,CAAWF,OAAO,IAAI,CAAtB,CAAf;AACA,QAAMV,gBAAgB,GAAGW,IAAI,CAACE,GAAL,CAASZ,iBAAT,EAA4B,CAA5B,CAAzB;AAEA,QAAMJ,IAAI,GAAGL,KAAK,CAACsB,OAAN,CAAmB,MAAM;AAClC,QAAI,CAACf,IAAL,EAAW,OAAOD,KAAP;;AAEX,QAAIA,KAAK,CAACiB,MAAN,KAAiBtB,WAAW,CAACuB,WAAjC,EAA8C;AAC1C,aAAO,CAAClB,KAAK,CAAC,CAAD,CAAN,EAAWA,KAAK,CAAC,CAAD,CAAhB,EAAqBA,KAAK,CAAC,CAAD,CAA1B,CAAP;AACH;;AAED,QAAIA,KAAK,CAACiB,MAAN,KAAiBtB,WAAW,CAACwB,WAAjC,EAA8C;AAC1C,aAAO,CAACnB,KAAK,CAAC,CAAD,CAAN,EAAWA,KAAK,CAAC,CAAD,CAAhB,EAAqBA,KAAK,CAAC,CAAD,CAA1B,EAA+BA,KAAK,CAAC,CAAD,CAApC,CAAP;AACH;;AAED,WAAOA,KAAP;AACH,GAZY,EAYV,CAACA,KAAD,EAAQC,IAAR,CAZU,CAAb;;AAcA,MAAIJ,KAAK,CAACuB,IAAN,KAAe,gBAAf,IAAmCvB,KAAK,CAACuB,IAAN,KAAe,kBAAtD,EAA0E;AAAA;;AACtE,QAAI,CAACvB,KAAK,CAACwB,UAAX,EAAuB;AACnBxB,MAAAA,KAAK,CAACwB,UAAN,GAAmB,EAAnB;AACH;;AACDxB,IAAAA,KAAK,CAACwB,UAAN,CAAiBC,UAAjB,iDACIzB,KAAK,CAACwB,UADV,sDACI,kBAAkBC,UADtB,yEACoCvB,IAAI,CAACkB,MAAL,GAAc,CADlD;AAEH;;AACD,SAAO,EACH,GAAGpB,KADA;AAEHC,IAAAA,YAFG;AAGHC,IAAAA,IAHG;AAIHE,IAAAA,IAJG;AAKHC,IAAAA,gBALG;AAMHE,IAAAA,uBANG;AAOHC,IAAAA,KAPG;AAQHC,IAAAA,sBARG;AASHC,IAAAA,aATG;AAUHC,IAAAA,UAVG;AAWHC,IAAAA,KAXG;AAYHE,IAAAA;AAZG,GAAP;AAcH","sourcesContent":["import React from 'react';\nimport { DATA_LENGTH } from '../constants';\nimport type { TCarouselProps } from '../types';\n\ntype TGetRequiredProps<P extends keyof TCarouselProps> = Record<\n P,\n Required<TCarouselProps>[P]\n>;\n\nexport type TInitializeCarouselProps<T> = TCarouselProps<T> &\n TGetRequiredProps<\n | 'defaultIndex'\n | 'loop'\n | 'width'\n | 'height'\n | 'scrollAnimationDuration'\n | 'autoPlayInterval'\n >;\n\nexport function useInitProps<T>(\n props: TCarouselProps<T>\n): TInitializeCarouselProps<T> {\n const {\n defaultIndex = 0,\n data: _data = [],\n loop = true,\n autoPlayInterval: _autoPlayInterval = 1000,\n scrollAnimationDuration = 500,\n style = {},\n panGestureHandlerProps = {},\n pagingEnabled = true,\n enableSnap = true,\n width: _width,\n height: _height,\n } = props;\n\n const width = Math.round(_width || 0);\n const height = Math.round(_height || 0);\n const autoPlayInterval = Math.max(_autoPlayInterval, 0);\n\n const data = React.useMemo<T[]>(() => {\n if (!loop) return _data;\n\n if (_data.length === DATA_LENGTH.SINGLE_ITEM) {\n return [_data[0], _data[0], _data[0]];\n }\n\n if (_data.length === DATA_LENGTH.DOUBLE_ITEM) {\n return [_data[0], _data[1], _data[0], _data[1]];\n }\n\n return _data;\n }, [_data, loop]);\n\n if (props.mode === 'vertical-stack' || props.mode === 'horizontal-stack') {\n if (!props.modeConfig) {\n props.modeConfig = {};\n }\n props.modeConfig.showLength =\n props.modeConfig?.showLength ?? data.length - 1;\n }\n return {\n ...props,\n defaultIndex,\n data,\n loop,\n autoPlayInterval,\n scrollAnimationDuration,\n style,\n panGestureHandlerProps,\n pagingEnabled,\n enableSnap,\n width,\n height,\n };\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type Animated from 'react-native-reanimated';
|
|
3
|
+
import type { TCarouselActionOptions } from '../types';
|
|
3
4
|
interface IOpts {
|
|
4
5
|
loop: boolean;
|
|
5
6
|
size: number;
|
|
@@ -17,11 +18,12 @@ export interface ICarouselController {
|
|
|
17
18
|
index: Animated.SharedValue<number>;
|
|
18
19
|
sharedIndex: React.MutableRefObject<number>;
|
|
19
20
|
sharedPreIndex: React.MutableRefObject<number>;
|
|
20
|
-
prev: () => void;
|
|
21
|
-
next: () => void;
|
|
21
|
+
prev: (opts?: TCarouselActionOptions) => void;
|
|
22
|
+
next: (opts?: TCarouselActionOptions) => void;
|
|
22
23
|
computedIndex: () => void;
|
|
23
24
|
getCurrentIndex: () => number;
|
|
24
25
|
to: (index: number, animated?: boolean) => void;
|
|
26
|
+
scrollTo: (opts?: TCarouselActionOptions) => void;
|
|
25
27
|
}
|
|
26
|
-
export declare function useCarouselController(
|
|
28
|
+
export declare function useCarouselController(options: IOpts): ICarouselController;
|
|
27
29
|
export {};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { TCarouselProps } from '../types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
loop: Required<TCarouselProps>['loop'];
|
|
5
|
-
width: Required<TCarouselProps>['width'];
|
|
6
|
-
height: Required<TCarouselProps>['height'];
|
|
7
|
-
};
|
|
2
|
+
declare type TGetRequiredProps<P extends keyof TCarouselProps> = Record<P, Required<TCarouselProps>[P]>;
|
|
3
|
+
export declare type TInitializeCarouselProps<T> = TCarouselProps<T> & TGetRequiredProps<'defaultIndex' | 'loop' | 'width' | 'height' | 'scrollAnimationDuration' | 'autoPlayInterval'>;
|
|
8
4
|
export declare function useInitProps<T>(props: TCarouselProps<T>): TInitializeCarouselProps<T>;
|
|
5
|
+
export {};
|
|
@@ -65,6 +65,11 @@ export declare type TCarouselProps<T = any> = {
|
|
|
65
65
|
* @description playback interval
|
|
66
66
|
*/
|
|
67
67
|
autoPlayInterval?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Time a scroll animation takes to finish
|
|
70
|
+
* @default 500 (ms)
|
|
71
|
+
*/
|
|
72
|
+
scrollAnimationDuration?: number;
|
|
68
73
|
/**
|
|
69
74
|
* Carousel container style
|
|
70
75
|
*/
|
|
@@ -125,21 +130,29 @@ export declare type TCarouselProps<T = any> = {
|
|
|
125
130
|
} & (TParallaxModeProps | TStackModeProps);
|
|
126
131
|
export interface ICarouselInstance {
|
|
127
132
|
/**
|
|
128
|
-
*
|
|
133
|
+
* Scroll to previous item, it takes one optional argument (count),
|
|
134
|
+
* which allows you to specify how many items to cross
|
|
129
135
|
*/
|
|
130
|
-
prev: () => void;
|
|
136
|
+
prev: (opts?: TCarouselActionOptions) => void;
|
|
131
137
|
/**
|
|
132
|
-
*
|
|
138
|
+
* Scroll to next item, it takes one optional argument (count),
|
|
139
|
+
* which allows you to specify how many items to cross
|
|
133
140
|
*/
|
|
134
|
-
next: () => void;
|
|
141
|
+
next: (opts?: TCarouselActionOptions) => void;
|
|
135
142
|
/**
|
|
136
143
|
* Get current item index
|
|
137
144
|
*/
|
|
138
145
|
getCurrentIndex: () => number;
|
|
139
146
|
/**
|
|
140
147
|
* Go to index
|
|
148
|
+
* @deprecated use scrollTo instead
|
|
141
149
|
*/
|
|
142
150
|
goToIndex: (index: number, animated?: boolean) => void;
|
|
151
|
+
/**
|
|
152
|
+
* Use value to scroll to a position where relative to the current position,
|
|
153
|
+
* scrollTo(-2) is equivalent to prev(2), scrollTo(2) is equivalent to next(2)
|
|
154
|
+
*/
|
|
155
|
+
scrollTo: (opts?: TCarouselActionOptions) => void;
|
|
143
156
|
}
|
|
144
157
|
export interface CarouselRenderItemInfo<ItemT> {
|
|
145
158
|
item: ItemT;
|
|
@@ -147,3 +160,8 @@ export interface CarouselRenderItemInfo<ItemT> {
|
|
|
147
160
|
animationValue: Animated.SharedValue<number>;
|
|
148
161
|
}
|
|
149
162
|
export declare type CarouselRenderItem<ItemT> = (info: CarouselRenderItemInfo<ItemT>) => React.ReactElement;
|
|
163
|
+
export interface TCarouselActionOptions {
|
|
164
|
+
count?: number;
|
|
165
|
+
animated?: boolean;
|
|
166
|
+
onFinished?: () => void;
|
|
167
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-reanimated-carousel",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Simple carousel component.fully implemented using Reanimated 2.Infinitely scrolling, very smooth.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -30,10 +30,13 @@
|
|
|
30
30
|
"release": "release-it --no-git.requireUpstream",
|
|
31
31
|
"ios": "yarn --cwd example ios",
|
|
32
32
|
"ios:pretty": "yarn --cwd example ios:pretty",
|
|
33
|
+
"web": "yarn --cwd example web",
|
|
34
|
+
"web:pretty": "yarn --cwd example web:pretty",
|
|
33
35
|
"android": "yarn --cwd example android",
|
|
34
36
|
"android:pretty": "yarn --cwd example android:pretty",
|
|
35
37
|
"pods": "cd example && pod-install --quiet",
|
|
36
|
-
"bootstrap": "yarn example && yarn && yarn pods"
|
|
38
|
+
"bootstrap": "yarn example && yarn && yarn pods",
|
|
39
|
+
"deploy": "cd example && yarn deploy"
|
|
37
40
|
},
|
|
38
41
|
"keywords": [
|
|
39
42
|
"react-native",
|
package/src/Carousel.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from 'react';
|
|
2
2
|
import Animated, { runOnJS, useDerivedValue } from 'react-native-reanimated';
|
|
3
|
+
|
|
3
4
|
import { useCarouselController } from './hooks/useCarouselController';
|
|
4
5
|
import { useAutoPlay } from './hooks/useAutoPlay';
|
|
5
6
|
import { usePropsErrorBoundary } from './hooks/usePropsErrorBoundary';
|
|
6
7
|
import { ScrollViewGesture } from './ScrollViewGesture';
|
|
7
8
|
import { useVisibleRanges } from './hooks/useVisibleRanges';
|
|
9
|
+
|
|
8
10
|
import type { ICarouselInstance, TCarouselProps } from './types';
|
|
9
11
|
import { StyleSheet, View } from 'react-native';
|
|
10
12
|
import { DATA_LENGTH } from './constants';
|
|
@@ -33,6 +35,7 @@ function Carousel<T>(
|
|
|
33
35
|
windowSize,
|
|
34
36
|
autoPlayReverse,
|
|
35
37
|
autoPlayInterval,
|
|
38
|
+
scrollAnimationDuration,
|
|
36
39
|
renderItem,
|
|
37
40
|
onScrollEnd,
|
|
38
41
|
onSnapToItem,
|
|
@@ -66,8 +69,8 @@ function Carousel<T>(
|
|
|
66
69
|
originalLength: data.length,
|
|
67
70
|
onScrollEnd: () => runOnJS(_onScrollEnd)(),
|
|
68
71
|
onScrollBegin: () => !!onScrollBegin && runOnJS(onScrollBegin)(),
|
|
69
|
-
onChange: (i) => onSnapToItem && runOnJS(onSnapToItem)(i),
|
|
70
|
-
duration:
|
|
72
|
+
onChange: (i) => !!onSnapToItem && runOnJS(onSnapToItem)(i),
|
|
73
|
+
duration: scrollAnimationDuration,
|
|
71
74
|
});
|
|
72
75
|
|
|
73
76
|
const {
|
|
@@ -79,7 +82,7 @@ function Carousel<T>(
|
|
|
79
82
|
getCurrentIndex,
|
|
80
83
|
} = carouselController;
|
|
81
84
|
|
|
82
|
-
const {
|
|
85
|
+
const { start, pause } = useAutoPlay({
|
|
83
86
|
autoPlay,
|
|
84
87
|
autoPlayInterval,
|
|
85
88
|
autoPlayReverse,
|
|
@@ -97,9 +100,9 @@ function Carousel<T>(
|
|
|
97
100
|
}, [sharedPreIndex, sharedIndex, computedIndex, onScrollEnd]);
|
|
98
101
|
|
|
99
102
|
const scrollViewGestureOnScrollEnd = React.useCallback(() => {
|
|
100
|
-
|
|
103
|
+
start();
|
|
101
104
|
_onScrollEnd();
|
|
102
|
-
}, [_onScrollEnd,
|
|
105
|
+
}, [_onScrollEnd, start]);
|
|
103
106
|
|
|
104
107
|
const goToIndex = React.useCallback(
|
|
105
108
|
(i: number, animated?: boolean) => {
|
|
@@ -115,8 +118,9 @@ function Carousel<T>(
|
|
|
115
118
|
prev,
|
|
116
119
|
getCurrentIndex,
|
|
117
120
|
goToIndex,
|
|
121
|
+
scrollTo: carouselController.scrollTo,
|
|
118
122
|
}),
|
|
119
|
-
[getCurrentIndex, goToIndex, next, prev]
|
|
123
|
+
[getCurrentIndex, goToIndex, next, prev, carouselController.scrollTo]
|
|
120
124
|
);
|
|
121
125
|
|
|
122
126
|
const visibleRanges = useVisibleRanges({
|
|
@@ -41,7 +41,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
41
41
|
enableSnap,
|
|
42
42
|
panGestureHandlerProps,
|
|
43
43
|
loop: infinite,
|
|
44
|
-
|
|
44
|
+
scrollAnimationDuration,
|
|
45
45
|
},
|
|
46
46
|
} = React.useContext(CTX);
|
|
47
47
|
|
|
@@ -59,7 +59,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
59
59
|
return withTiming(
|
|
60
60
|
toValue,
|
|
61
61
|
{
|
|
62
|
-
duration:
|
|
62
|
+
duration: scrollAnimationDuration,
|
|
63
63
|
easing: Easing.easeOutQuart,
|
|
64
64
|
},
|
|
65
65
|
(isFinished) => {
|
|
@@ -69,7 +69,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
|
|
|
69
69
|
}
|
|
70
70
|
);
|
|
71
71
|
},
|
|
72
|
-
[
|
|
72
|
+
[scrollAnimationDuration]
|
|
73
73
|
);
|
|
74
74
|
|
|
75
75
|
const endWithSpring = React.useCallback(
|
package/src/hooks/useAutoPlay.ts
CHANGED
|
@@ -15,44 +15,41 @@ export function useAutoPlay(opts: {
|
|
|
15
15
|
} = opts;
|
|
16
16
|
|
|
17
17
|
const timer = React.useRef<NodeJS.Timer>();
|
|
18
|
+
const stopped = React.useRef<boolean>(!autoPlay);
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
const run = React.useCallback(() => {
|
|
24
|
-
if (timer.current) {
|
|
25
|
-
pause();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!autoPlay) {
|
|
20
|
+
const play = React.useCallback(() => {
|
|
21
|
+
if (stopped.current) {
|
|
29
22
|
return;
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
timer.current =
|
|
25
|
+
timer.current = setTimeout(() => {
|
|
33
26
|
autoPlayReverse
|
|
34
|
-
? carouselController.prev()
|
|
35
|
-
: carouselController.next();
|
|
27
|
+
? carouselController.prev({ onFinished: play })
|
|
28
|
+
: carouselController.next({ onFinished: play });
|
|
36
29
|
}, autoPlayInterval);
|
|
37
|
-
}, [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
}, [autoPlayReverse, autoPlayInterval, carouselController]);
|
|
31
|
+
|
|
32
|
+
const pause = React.useCallback(() => {
|
|
33
|
+
timer.current && clearInterval(timer.current);
|
|
34
|
+
stopped.current = true;
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const start = React.useCallback(() => {
|
|
38
|
+
stopped.current = false;
|
|
39
|
+
play();
|
|
40
|
+
}, [play]);
|
|
44
41
|
|
|
45
42
|
React.useEffect(() => {
|
|
46
43
|
if (autoPlay) {
|
|
47
|
-
|
|
44
|
+
start();
|
|
48
45
|
} else {
|
|
49
46
|
pause();
|
|
50
47
|
}
|
|
51
48
|
return pause;
|
|
52
|
-
}, [
|
|
49
|
+
}, [pause, start, autoPlay]);
|
|
53
50
|
|
|
54
51
|
return {
|
|
55
|
-
run,
|
|
56
52
|
pause,
|
|
53
|
+
start,
|
|
57
54
|
};
|
|
58
55
|
}
|
|
@@ -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,12 +2,20 @@ 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
|
+
>;
|
|
11
19
|
|
|
12
20
|
export function useInitProps<T>(
|
|
13
21
|
props: TCarouselProps<T>
|
|
@@ -16,7 +24,8 @@ export function useInitProps<T>(
|
|
|
16
24
|
defaultIndex = 0,
|
|
17
25
|
data: _data = [],
|
|
18
26
|
loop = true,
|
|
19
|
-
autoPlayInterval = 1000,
|
|
27
|
+
autoPlayInterval: _autoPlayInterval = 1000,
|
|
28
|
+
scrollAnimationDuration = 500,
|
|
20
29
|
style = {},
|
|
21
30
|
panGestureHandlerProps = {},
|
|
22
31
|
pagingEnabled = true,
|
|
@@ -27,6 +36,7 @@ export function useInitProps<T>(
|
|
|
27
36
|
|
|
28
37
|
const width = Math.round(_width || 0);
|
|
29
38
|
const height = Math.round(_height || 0);
|
|
39
|
+
const autoPlayInterval = Math.max(_autoPlayInterval, 0);
|
|
30
40
|
|
|
31
41
|
const data = React.useMemo<T[]>(() => {
|
|
32
42
|
if (!loop) return _data;
|
|
@@ -55,6 +65,7 @@ export function useInitProps<T>(
|
|
|
55
65
|
data,
|
|
56
66
|
loop,
|
|
57
67
|
autoPlayInterval,
|
|
68
|
+
scrollAnimationDuration,
|
|
58
69
|
style,
|
|
59
70
|
panGestureHandlerProps,
|
|
60
71
|
pagingEnabled,
|
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
|
+
}
|