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.
Files changed (41) hide show
  1. package/README.md +9 -6
  2. package/README.zh-CN.md +10 -4
  3. package/lib/commonjs/Carousel.js +9 -7
  4. package/lib/commonjs/Carousel.js.map +1 -1
  5. package/lib/commonjs/ScrollViewGesture.js +3 -3
  6. package/lib/commonjs/ScrollViewGesture.js.map +1 -1
  7. package/lib/commonjs/hooks/useAutoPlay.js +22 -16
  8. package/lib/commonjs/hooks/useAutoPlay.js.map +1 -1
  9. package/lib/commonjs/hooks/useCarouselController.js +84 -24
  10. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  11. package/lib/commonjs/hooks/useInitProps.js +4 -1
  12. package/lib/commonjs/hooks/useInitProps.js.map +1 -1
  13. package/lib/module/Carousel.js +9 -7
  14. package/lib/module/Carousel.js.map +1 -1
  15. package/lib/module/ScrollViewGesture.js +3 -3
  16. package/lib/module/ScrollViewGesture.js.map +1 -1
  17. package/lib/module/hooks/useAutoPlay.js +22 -16
  18. package/lib/module/hooks/useAutoPlay.js.map +1 -1
  19. package/lib/module/hooks/useCarouselController.js +82 -24
  20. package/lib/module/hooks/useCarouselController.js.map +1 -1
  21. package/lib/module/hooks/useInitProps.js +4 -1
  22. package/lib/module/hooks/useInitProps.js.map +1 -1
  23. package/lib/typescript/hooks/useAutoPlay.d.ts +1 -1
  24. package/lib/typescript/hooks/useCarouselController.d.ts +5 -3
  25. package/lib/typescript/hooks/useInitProps.d.ts +3 -6
  26. package/lib/typescript/types.d.ts +22 -4
  27. package/package.json +5 -2
  28. package/src/Carousel.tsx +10 -6
  29. package/src/ScrollViewGesture.tsx +3 -3
  30. package/src/hooks/useAutoPlay.ts +20 -23
  31. package/src/hooks/useCarouselController.tsx +101 -42
  32. package/src/hooks/useInitProps.ts +18 -7
  33. package/src/types.ts +23 -4
  34. package/CHANGELOG.md +0 -361
  35. package/lib/commonjs/hooks/useIndexController.js +0 -65
  36. package/lib/commonjs/hooks/useIndexController.js.map +0 -1
  37. package/lib/module/hooks/useIndexController.js +0 -52
  38. package/lib/module/hooks/useIndexController.js.map +0 -1
  39. package/lib/typescript/hooks/useIndexController.d.ts +0 -18
  40. package/src/.DS_Store +0 -0
  41. 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;AAUA,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,GAAG,IAJjB;AAKFC,IAAAA,KAAK,GAAG,EALN;AAMFC,IAAAA,sBAAsB,GAAG,EANvB;AAOFC,IAAAA,aAAa,GAAG,IAPd;AAQFC,IAAAA,UAAU,GAAG,IARX;AASFC,IAAAA,KAAK,EAAEC,MATL;AAUFC,IAAAA,MAAM,EAAEC;AAVN,MAWFb,KAXJ;AAaA,QAAMU,KAAK,GAAGI,IAAI,CAACC,KAAL,CAAWJ,MAAM,IAAI,CAArB,CAAd;AACA,QAAMC,MAAM,GAAGE,IAAI,CAACC,KAAL,CAAWF,OAAO,IAAI,CAAtB,CAAf;AAEA,QAAMX,IAAI,GAAGL,KAAK,CAACmB,OAAN,CAAmB,MAAM;AAClC,QAAI,CAACZ,IAAL,EAAW,OAAOD,KAAP;;AAEX,QAAIA,KAAK,CAACc,MAAN,KAAiBnB,WAAW,CAACoB,WAAjC,EAA8C;AAC1C,aAAO,CAACf,KAAK,CAAC,CAAD,CAAN,EAAWA,KAAK,CAAC,CAAD,CAAhB,EAAqBA,KAAK,CAAC,CAAD,CAA1B,CAAP;AACH;;AAED,QAAIA,KAAK,CAACc,MAAN,KAAiBnB,WAAW,CAACqB,WAAjC,EAA8C;AAC1C,aAAO,CAAChB,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,CAACoB,IAAN,KAAe,gBAAf,IAAmCpB,KAAK,CAACoB,IAAN,KAAe,kBAAtD,EAA0E;AAAA;;AACtE,QAAI,CAACpB,KAAK,CAACqB,UAAX,EAAuB;AACnBrB,MAAAA,KAAK,CAACqB,UAAN,GAAmB,EAAnB;AACH;;AACDrB,IAAAA,KAAK,CAACqB,UAAN,CAAiBC,UAAjB,iDACItB,KAAK,CAACqB,UADV,sDACI,kBAAkBC,UADtB,yEACoCpB,IAAI,CAACe,MAAL,GAAc,CADlD;AAEH;;AACD,SAAO,EACH,GAAGjB,KADA;AAEHC,IAAAA,YAFG;AAGHC,IAAAA,IAHG;AAIHE,IAAAA,IAJG;AAKHC,IAAAA,gBALG;AAMHC,IAAAA,KANG;AAOHC,IAAAA,sBAPG;AAQHC,IAAAA,aARG;AASHC,IAAAA,UATG;AAUHC,IAAAA,KAVG;AAWHE,IAAAA;AAXG,GAAP;AAaH","sourcesContent":["import React from 'react';\nimport { DATA_LENGTH } from '../constants';\nimport type { TCarouselProps } from '../types';\n\nexport type TInitializeCarouselProps<T> = TCarouselProps<T> & {\n defaultIndex: Required<TCarouselProps>['defaultIndex'];\n loop: Required<TCarouselProps>['loop'];\n width: Required<TCarouselProps>['width'];\n height: Required<TCarouselProps>['height'];\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 = 1000,\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\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 style,\n panGestureHandlerProps,\n pagingEnabled,\n enableSnap,\n width,\n height,\n };\n}\n"]}
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"]}
@@ -5,6 +5,6 @@ export declare function useAutoPlay(opts: {
5
5
  autoPlayReverse?: boolean;
6
6
  carouselController: ICarouselController;
7
7
  }): {
8
- run: () => void;
9
8
  pause: () => void;
9
+ start: () => void;
10
10
  };
@@ -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(opts: IOpts): ICarouselController;
28
+ export declare function useCarouselController(options: IOpts): ICarouselController;
27
29
  export {};
@@ -1,8 +1,5 @@
1
1
  import type { TCarouselProps } from '../types';
2
- export declare type TInitializeCarouselProps<T> = TCarouselProps<T> & {
3
- defaultIndex: Required<TCarouselProps>['defaultIndex'];
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
- * Play the last one
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
- * Play the next one
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.2",
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: autoPlayInterval,
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 { run, pause } = useAutoPlay({
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
- run();
103
+ start();
101
104
  _onScrollEnd();
102
- }, [_onScrollEnd, run]);
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
- autoPlayInterval,
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: autoPlayInterval,
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
- [autoPlayInterval]
72
+ [scrollAnimationDuration]
73
73
  );
74
74
 
75
75
  const endWithSpring = React.useCallback(
@@ -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 pause = React.useCallback(() => {
20
- timer.current && clearInterval(timer.current);
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 = setInterval(() => {
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
- pause,
39
- autoPlay,
40
- autoPlayReverse,
41
- autoPlayInterval,
42
- carouselController,
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
- run();
44
+ start();
48
45
  } else {
49
46
  pause();
50
47
  }
51
48
  return pause;
52
- }, [run, pause, autoPlay]);
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(opts: IOpts): ICarouselController {
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
- } = opts;
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
- opts.onScrollEnd?.();
96
- }, [opts]);
110
+ options.onScrollEnd?.();
111
+ }, [options]);
97
112
 
98
113
  const onScrollBegin = React.useCallback(() => {
99
- opts.onScrollBegin?.();
100
- }, [opts]);
114
+ options.onScrollBegin?.();
115
+ }, [options]);
101
116
 
102
117
  const scrollWithTiming = React.useCallback(
103
- (toValue: number, callback?: () => void) => {
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
- if (!canSliding() || (!loop && index.value === length - 1)) return;
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
- onScrollBegin?.();
138
+ onScrollBegin?.();
122
139
 
123
- const currentPage = Math.round(handlerOffsetX.value / size);
140
+ const nextPage = currentFixedPage() + count;
141
+ index.value = nextPage;
124
142
 
125
- handlerOffsetX.value = scrollWithTiming((currentPage - 1) * size);
126
- }, [
127
- canSliding,
128
- loop,
129
- index.value,
130
- length,
131
- onScrollBegin,
132
- handlerOffsetX,
133
- size,
134
- scrollWithTiming,
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
- if (!canSliding() || (!loop && index.value === 0)) return;
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
- onScrollBegin?.();
171
+ onScrollBegin?.();
141
172
 
142
- const currentPage = Math.round(handlerOffsetX.value / size);
173
+ const prevPage = currentFixedPage() - count;
174
+ index.value = prevPage;
143
175
 
144
- handlerOffsetX.value = scrollWithTiming((currentPage + 1) * size);
145
- }, [
146
- canSliding,
147
- loop,
148
- index.value,
149
- onScrollBegin,
150
- handlerOffsetX,
151
- size,
152
- scrollWithTiming,
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
- handlerOffsetX.value = scrollWithTiming(offset, () => {
166
- index.value = idx;
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
- export type TInitializeCarouselProps<T> = TCarouselProps<T> & {
6
- defaultIndex: Required<TCarouselProps>['defaultIndex'];
7
- loop: Required<TCarouselProps>['loop'];
8
- width: Required<TCarouselProps>['width'];
9
- height: Required<TCarouselProps>['height'];
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
- * Play the last one
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
- * Play the next one
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
+ }