react-native-reanimated-carousel 2.3.9 → 2.3.10

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 (30) hide show
  1. package/lib/commonjs/Carousel.js +1 -1
  2. package/lib/commonjs/Carousel.js.map +1 -1
  3. package/lib/commonjs/ScrollViewGesture.js +1 -1
  4. package/lib/commonjs/ScrollViewGesture.js.map +1 -1
  5. package/lib/commonjs/hooks/useAutoPlay.js +1 -1
  6. package/lib/commonjs/hooks/useAutoPlay.js.map +1 -1
  7. package/lib/commonjs/hooks/useCarouselController.js +1 -1
  8. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  9. package/lib/commonjs/utils/computedWithAutoFillData.js +1 -1
  10. package/lib/commonjs/utils/computedWithAutoFillData.js.map +1 -1
  11. package/lib/module/Carousel.js +40 -25
  12. package/lib/module/Carousel.js.map +1 -1
  13. package/lib/module/ScrollViewGesture.js +8 -10
  14. package/lib/module/ScrollViewGesture.js.map +1 -1
  15. package/lib/module/hooks/useAutoPlay.js +7 -3
  16. package/lib/module/hooks/useAutoPlay.js.map +1 -1
  17. package/lib/module/hooks/useCarouselController.js +36 -43
  18. package/lib/module/hooks/useCarouselController.js.map +1 -1
  19. package/lib/module/utils/computedWithAutoFillData.js +25 -0
  20. package/lib/module/utils/computedWithAutoFillData.js.map +1 -1
  21. package/lib/typescript/hooks/useCarouselController.d.ts +4 -10
  22. package/lib/typescript/types.d.ts +4 -0
  23. package/lib/typescript/utils/computedWithAutoFillData.d.ts +4 -0
  24. package/package.json +3 -3
  25. package/src/Carousel.tsx +43 -29
  26. package/src/ScrollViewGesture.tsx +7 -9
  27. package/src/hooks/useAutoPlay.ts +4 -3
  28. package/src/hooks/useCarouselController.tsx +53 -62
  29. package/src/types.ts +4 -0
  30. package/src/utils/computedWithAutoFillData.ts +18 -0
@@ -1,28 +1,22 @@
1
- import React from 'react';
2
1
  import type Animated from 'react-native-reanimated';
3
2
  import type { TCarouselActionOptions, TCarouselProps } from '../types';
4
3
  interface IOpts {
5
4
  loop: boolean;
6
5
  size: number;
6
+ data: TCarouselProps['data'];
7
+ autoFillData: TCarouselProps['autoFillData'];
7
8
  handlerOffsetX: Animated.SharedValue<number>;
8
9
  withAnimation?: TCarouselProps['withAnimation'];
9
- disable?: boolean;
10
10
  duration?: number;
11
- originalLength: number;
12
- length: number;
13
11
  defaultIndex?: number;
14
12
  onScrollBegin?: () => void;
15
13
  onScrollEnd?: () => void;
16
- onChange: (index: number) => void;
17
14
  }
18
15
  export interface ICarouselController {
19
- length: number;
20
- index: Animated.SharedValue<number>;
21
- sharedIndex: React.MutableRefObject<number>;
22
- sharedPreIndex: React.MutableRefObject<number>;
16
+ sharedIndex: Animated.SharedValue<number>;
17
+ sharedPreIndex: Animated.SharedValue<number>;
23
18
  prev: (opts?: TCarouselActionOptions) => void;
24
19
  next: (opts?: TCarouselActionOptions) => void;
25
- computedIndex: () => void;
26
20
  getCurrentIndex: () => number;
27
21
  to: (index: number, animated?: boolean) => void;
28
22
  scrollTo: (opts?: TCarouselActionOptions) => void;
@@ -129,6 +129,10 @@ export declare type TCarouselProps<T = any> = {
129
129
  * Specifies the scrolling animation effect.
130
130
  */
131
131
  withAnimation?: WithAnimation;
132
+ /**
133
+ * Used to locate this view in end-to-end tests.
134
+ */
135
+ testID?: string;
132
136
  /**
133
137
  * Custom carousel config.
134
138
  */
@@ -2,6 +2,10 @@ declare type BaseParams<T extends object = {}> = {
2
2
  autoFillData: boolean;
3
3
  loop: boolean;
4
4
  } & T;
5
+ export declare function convertToSharedIndex(params: BaseParams<{
6
+ index: number;
7
+ rawDataLength: number;
8
+ }>): number;
5
9
  export declare function computedOffsetXValueWithAutoFillData(params: BaseParams<{
6
10
  rawDataLength: number;
7
11
  value: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reanimated-carousel",
3
- "version": "2.3.9",
3
+ "version": "2.3.10",
4
4
  "description": "Simple carousel component.fully implemented using Reanimated 2.Infinitely scrolling, very smooth.",
5
5
  "main": "lib/commonjs/index",
6
6
  "types": "lib/typescript/index.d.ts",
@@ -26,8 +26,8 @@
26
26
  "lint": "eslint \"src/**/*.{js,ts,tsx}\"",
27
27
  "dev": "yarn watch 'yarn prepare' ./src",
28
28
  "prepare": "bob build",
29
- "release": "release-it --no-git.requireUpstream",
30
- "preRelease": "release-it --no-git.requireUpstream --preRelease=beta",
29
+ "release": "yarn prepare && release-it --no-git.requireUpstream",
30
+ "preRelease": "yarn prepare && release-it --no-git.requireUpstream --preRelease=beta",
31
31
  "ios": "yarn --cwd example ios",
32
32
  "ios:pretty": "yarn --cwd example ios:pretty",
33
33
  "web": "yarn --cwd example web",
package/src/Carousel.tsx CHANGED
@@ -1,5 +1,9 @@
1
1
  import React from 'react';
2
- import Animated, { runOnJS, useDerivedValue } from 'react-native-reanimated';
2
+ import Animated, {
3
+ runOnJS,
4
+ runOnUI,
5
+ useDerivedValue,
6
+ } from 'react-native-reanimated';
3
7
 
4
8
  import { useCarouselController } from './hooks/useCarouselController';
5
9
  import { useAutoPlay } from './hooks/useAutoPlay';
@@ -22,6 +26,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
22
26
  const props = useInitProps(_props);
23
27
 
24
28
  const {
29
+ testID,
25
30
  data,
26
31
  rawData,
27
32
  loop,
@@ -73,28 +78,27 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
73
78
  const carouselController = useCarouselController({
74
79
  loop,
75
80
  size,
81
+ data,
82
+ autoFillData,
76
83
  handlerOffsetX,
77
- length: data.length,
78
- disable: !data.length,
79
84
  withAnimation,
80
- originalLength: data.length,
81
85
  defaultIndex,
82
86
  onScrollEnd: () => runOnJS(_onScrollEnd)(),
83
87
  onScrollBegin: () => !!onScrollBegin && runOnJS(onScrollBegin)(),
84
- onChange: (i) => !!onSnapToItem && runOnJS(onSnapToItem)(i),
85
88
  duration: scrollAnimationDuration,
86
89
  });
87
90
 
88
91
  const {
92
+ sharedIndex,
93
+ sharedPreIndex,
94
+ to,
89
95
  next,
90
96
  prev,
91
- sharedPreIndex,
92
- sharedIndex,
93
- computedIndex,
97
+ scrollTo,
94
98
  getCurrentIndex,
95
99
  } = carouselController;
96
100
 
97
- const { start, pause } = useAutoPlay({
101
+ const { start: startAutoPlay, pause: pauseAutoPlay } = useAutoPlay({
98
102
  autoPlay,
99
103
  autoPlayInterval,
100
104
  autoPlayReverse,
@@ -102,29 +106,44 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
102
106
  });
103
107
 
104
108
  const _onScrollEnd = React.useCallback(() => {
105
- computedIndex();
106
- onScrollEnd?.(sharedPreIndex.current, sharedIndex.current);
107
- }, [sharedPreIndex, sharedIndex, computedIndex, onScrollEnd]);
109
+ 'worklet';
110
+ const _sharedIndex = Math.round(sharedIndex.value);
111
+ const _sharedPreIndex = Math.round(sharedPreIndex.value);
112
+
113
+ if (onSnapToItem) {
114
+ runOnJS(onSnapToItem)(_sharedIndex);
115
+ }
116
+ if (onScrollEnd) {
117
+ runOnJS(onScrollEnd)(_sharedPreIndex, _sharedIndex);
118
+ }
119
+ }, [onSnapToItem, onScrollEnd, sharedIndex, sharedPreIndex]);
108
120
 
109
121
  const scrollViewGestureOnScrollBegin = React.useCallback(() => {
110
- pause();
122
+ pauseAutoPlay();
111
123
  onScrollBegin?.();
112
- }, [onScrollBegin, pause]);
124
+ }, [onScrollBegin, pauseAutoPlay]);
113
125
 
114
126
  const scrollViewGestureOnScrollEnd = React.useCallback(() => {
115
- start();
116
- _onScrollEnd();
117
- }, [_onScrollEnd, start]);
127
+ startAutoPlay();
128
+ /**
129
+ * TODO magic
130
+ */
131
+ runOnUI(_onScrollEnd)();
132
+ }, [_onScrollEnd, startAutoPlay]);
118
133
 
119
- const scrollViewGestureOnTouchBegin = React.useCallback(pause, [pause]);
134
+ const scrollViewGestureOnTouchBegin = React.useCallback(pauseAutoPlay, [
135
+ pauseAutoPlay,
136
+ ]);
120
137
 
121
- const scrollViewGestureOnTouchEnd = React.useCallback(start, [start]);
138
+ const scrollViewGestureOnTouchEnd = React.useCallback(startAutoPlay, [
139
+ startAutoPlay,
140
+ ]);
122
141
 
123
142
  const goToIndex = React.useCallback(
124
143
  (i: number, animated?: boolean) => {
125
- carouselController.to(i, animated);
144
+ to(i, animated);
126
145
  },
127
- [carouselController]
146
+ [to]
128
147
  );
129
148
 
130
149
  React.useImperativeHandle(
@@ -134,15 +153,9 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
134
153
  prev,
135
154
  getCurrentIndex,
136
155
  goToIndex,
137
- scrollTo: carouselController.scrollTo,
156
+ scrollTo,
138
157
  }),
139
- [
140
- getCurrentIndex,
141
- goToIndex,
142
- next,
143
- prev,
144
- carouselController.scrollTo,
145
- ]
158
+ [getCurrentIndex, goToIndex, next, prev, scrollTo]
146
159
  );
147
160
 
148
161
  const visibleRanges = useVisibleRanges({
@@ -201,6 +214,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
201
214
  { width: width || '100%', height: height || '100%' },
202
215
  style,
203
216
  ]}
217
+ testID={testID}
204
218
  >
205
219
  <ScrollViewGesture
206
220
  size={size}
@@ -68,24 +68,22 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
68
68
  const _withSpring = React.useCallback(
69
69
  (toValue: number, onFinished?: () => void) => {
70
70
  'worklet';
71
- const callback = (isFinished: boolean) => {
72
- 'worklet';
73
- if (isFinished) {
74
- onFinished && runOnJS(onFinished)();
75
- }
76
- };
77
-
78
71
  const defaultWithAnimation: WithTimingAnimation = {
79
72
  type: 'timing',
80
73
  config: {
81
- duration: scrollAnimationDuration,
74
+ duration: scrollAnimationDuration + 100,
82
75
  easing: Easing.easeOutQuart,
83
76
  },
84
77
  };
85
78
 
86
79
  return dealWithAnimation(withAnimation ?? defaultWithAnimation)(
87
80
  toValue,
88
- callback
81
+ (isFinished: boolean) => {
82
+ 'worklet';
83
+ if (isFinished) {
84
+ onFinished && runOnJS(onFinished)();
85
+ }
86
+ }
89
87
  );
90
88
  },
91
89
  [scrollAnimationDuration, withAnimation]
@@ -14,6 +14,7 @@ export function useAutoPlay(opts: {
14
14
  carouselController,
15
15
  } = opts;
16
16
 
17
+ const { prev, next } = carouselController;
17
18
  const timer = React.useRef<NodeJS.Timer>();
18
19
  const stopped = React.useRef<boolean>(!autoPlay);
19
20
 
@@ -25,10 +26,10 @@ export function useAutoPlay(opts: {
25
26
  timer.current && clearTimeout(timer.current);
26
27
  timer.current = setTimeout(() => {
27
28
  autoPlayReverse
28
- ? carouselController.prev({ onFinished: play })
29
- : carouselController.next({ onFinished: play });
29
+ ? prev({ onFinished: play })
30
+ : next({ onFinished: play });
30
31
  }, autoPlayInterval);
31
- }, [autoPlayReverse, autoPlayInterval, carouselController]);
32
+ }, [autoPlayReverse, autoPlayInterval, prev, next]);
32
33
 
33
34
  const pause = React.useCallback(() => {
34
35
  if (!autoPlay) {
@@ -1,38 +1,37 @@
1
1
  import React from 'react';
2
2
  import type Animated from 'react-native-reanimated';
3
3
  import { Easing } from '../constants';
4
- import { runOnJS, useSharedValue } from 'react-native-reanimated';
4
+ import {
5
+ runOnJS,
6
+ useAnimatedReaction,
7
+ useSharedValue,
8
+ } from 'react-native-reanimated';
5
9
  import type {
6
10
  TCarouselActionOptions,
7
11
  TCarouselProps,
8
12
  WithTimingAnimation,
9
13
  } from '../types';
10
14
  import { dealWithAnimation } from '@/utils/dealWithAnimation';
15
+ import { convertToSharedIndex } from '@/utils/computedWithAutoFillData';
11
16
 
12
17
  interface IOpts {
13
18
  loop: boolean;
14
19
  size: number;
20
+ data: TCarouselProps['data'];
21
+ autoFillData: TCarouselProps['autoFillData'];
15
22
  handlerOffsetX: Animated.SharedValue<number>;
16
23
  withAnimation?: TCarouselProps['withAnimation'];
17
- disable?: boolean;
18
24
  duration?: number;
19
- originalLength: number;
20
- length: number;
21
25
  defaultIndex?: number;
22
26
  onScrollBegin?: () => void;
23
27
  onScrollEnd?: () => void;
24
- // the length before fill data
25
- onChange: (index: number) => void;
26
28
  }
27
29
 
28
30
  export interface ICarouselController {
29
- length: number;
30
- index: Animated.SharedValue<number>;
31
- sharedIndex: React.MutableRefObject<number>;
32
- sharedPreIndex: React.MutableRefObject<number>;
31
+ sharedIndex: Animated.SharedValue<number>;
32
+ sharedPreIndex: Animated.SharedValue<number>;
33
33
  prev: (opts?: TCarouselActionOptions) => void;
34
34
  next: (opts?: TCarouselActionOptions) => void;
35
- computedIndex: () => void;
36
35
  getCurrentIndex: () => number;
37
36
  to: (index: number, animated?: boolean) => void;
38
37
  scrollTo: (opts?: TCarouselActionOptions) => void;
@@ -41,70 +40,64 @@ export interface ICarouselController {
41
40
  export function useCarouselController(options: IOpts): ICarouselController {
42
41
  const {
43
42
  size,
43
+ data,
44
44
  loop,
45
45
  handlerOffsetX,
46
46
  withAnimation,
47
- disable = false,
48
- originalLength,
49
- length,
50
- onChange,
51
- duration,
52
47
  defaultIndex = 0,
48
+ duration,
49
+ autoFillData,
53
50
  } = options;
54
51
 
52
+ const dataInfo = React.useMemo(
53
+ () => ({
54
+ length: data.length,
55
+ disable: !data.length,
56
+ originalLength: data.length,
57
+ }),
58
+ [data]
59
+ );
60
+
55
61
  const index = useSharedValue<number>(defaultIndex);
56
62
  // The Index displayed to the user
57
- const sharedIndex = React.useRef<number>(defaultIndex);
58
- const sharedPreIndex = React.useRef<number>(defaultIndex);
63
+ const sharedIndex = useSharedValue<number>(defaultIndex);
64
+ const sharedPreIndex = useSharedValue<number>(defaultIndex);
59
65
 
60
66
  const currentFixedPage = React.useCallback(() => {
61
67
  if (loop) {
62
68
  return -Math.round(handlerOffsetX.value / size);
63
69
  }
64
70
 
65
- const fixed = (handlerOffsetX.value / size) % length;
71
+ const fixed = (handlerOffsetX.value / size) % dataInfo.length;
66
72
  return Math.round(
67
73
  handlerOffsetX.value <= 0
68
74
  ? Math.abs(fixed)
69
- : Math.abs(fixed > 0 ? length - fixed : 0)
75
+ : Math.abs(fixed > 0 ? dataInfo.length - fixed : 0)
70
76
  );
71
- }, [handlerOffsetX, length, size, loop]);
77
+ }, [handlerOffsetX, dataInfo, size, loop]);
72
78
 
73
- const convertToSharedIndex = React.useCallback(
74
- (i: number) => {
75
- if (loop) {
76
- switch (originalLength) {
77
- case 1:
78
- return 0;
79
- case 2:
80
- return i % 2;
81
- }
82
- }
83
- return i;
79
+ const computedIndex = React.useCallback(
80
+ (handlerOffsetXValue: number) => {
81
+ 'worklet';
82
+ sharedPreIndex.value = sharedIndex.value;
83
+ const toInt = (handlerOffsetXValue / size) % dataInfo.length;
84
+ const isPositive = handlerOffsetXValue <= 0;
85
+ const i = isPositive
86
+ ? Math.abs(toInt)
87
+ : Math.abs(toInt > 0 ? dataInfo.length - toInt : 0);
88
+ index.value = i;
89
+ sharedIndex.value = convertToSharedIndex({
90
+ loop,
91
+ rawDataLength: dataInfo.originalLength,
92
+ autoFillData: autoFillData!,
93
+ index: i,
94
+ });
84
95
  },
85
- [originalLength, loop]
96
+ [sharedPreIndex, sharedIndex, size, dataInfo, index, loop, autoFillData]
86
97
  );
87
98
 
88
- const computedIndex = React.useCallback(() => {
89
- sharedPreIndex.current = sharedIndex.current;
90
- const toInt = (handlerOffsetX.value / size) % length;
91
- const i =
92
- handlerOffsetX.value <= 0
93
- ? Math.abs(toInt)
94
- : Math.abs(toInt > 0 ? length - toInt : 0);
95
- index.value = i;
96
- const _sharedIndex = convertToSharedIndex(i);
97
- sharedIndex.current = _sharedIndex;
98
- onChange(_sharedIndex);
99
- }, [
100
- length,
99
+ useAnimatedReaction(() => handlerOffsetX.value, computedIndex, [
101
100
  handlerOffsetX,
102
- sharedPreIndex,
103
- index,
104
- size,
105
- sharedIndex,
106
- convertToSharedIndex,
107
- onChange,
108
101
  ]);
109
102
 
110
103
  const getCurrentIndex = React.useCallback(() => {
@@ -112,8 +105,8 @@ export function useCarouselController(options: IOpts): ICarouselController {
112
105
  }, [index]);
113
106
 
114
107
  const canSliding = React.useCallback(() => {
115
- return !disable;
116
- }, [disable]);
108
+ return !dataInfo.disable;
109
+ }, [dataInfo]);
117
110
 
118
111
  const onScrollEnd = React.useCallback(() => {
119
112
  options.onScrollEnd?.();
@@ -151,7 +144,8 @@ export function useCarouselController(options: IOpts): ICarouselController {
151
144
  (opts: TCarouselActionOptions = {}) => {
152
145
  'worklet';
153
146
  const { count = 1, animated = true, onFinished } = opts;
154
- if (!canSliding() || (!loop && index.value >= length - 1)) return;
147
+ if (!canSliding() || (!loop && index.value >= dataInfo.length - 1))
148
+ return;
155
149
 
156
150
  onScrollBegin?.();
157
151
 
@@ -172,7 +166,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
172
166
  canSliding,
173
167
  loop,
174
168
  index,
175
- length,
169
+ dataInfo,
176
170
  onScrollBegin,
177
171
  handlerOffsetX,
178
172
  size,
@@ -259,15 +253,12 @@ export function useCarouselController(options: IOpts): ICarouselController {
259
253
  );
260
254
 
261
255
  return {
256
+ sharedIndex,
257
+ sharedPreIndex,
258
+ to,
262
259
  next,
263
260
  prev,
264
- to,
265
261
  scrollTo,
266
- index,
267
- length,
268
- sharedIndex,
269
- sharedPreIndex,
270
- computedIndex,
271
262
  getCurrentIndex,
272
263
  };
273
264
  }
package/src/types.ts CHANGED
@@ -144,6 +144,10 @@ export type TCarouselProps<T = any> = {
144
144
  * Specifies the scrolling animation effect.
145
145
  */
146
146
  withAnimation?: WithAnimation;
147
+ /**
148
+ * Used to locate this view in end-to-end tests.
149
+ */
150
+ testID?: string;
147
151
  /**
148
152
  * Custom carousel config.
149
153
  */
@@ -12,6 +12,24 @@ type BaseParams<T extends object = {}> = {
12
12
  loop: boolean;
13
13
  } & T;
14
14
 
15
+ export function convertToSharedIndex(
16
+ params: BaseParams<{ index: number; rawDataLength: number }>
17
+ ) {
18
+ 'worklet';
19
+ const { loop, rawDataLength, index, autoFillData } = params;
20
+
21
+ if (isAutoFillData({ loop, autoFillData })) {
22
+ switch (rawDataLength) {
23
+ case SINGLE_ITEM:
24
+ return 0;
25
+ case DOUBLE_ITEM:
26
+ return index % 2;
27
+ }
28
+ }
29
+
30
+ return index;
31
+ }
32
+
15
33
  export function computedOffsetXValueWithAutoFillData(
16
34
  params: BaseParams<{
17
35
  rawDataLength: number;