react-native-reanimated-carousel 2.1.1 → 2.2.3

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 (53) hide show
  1. package/README.md +9 -6
  2. package/README.zh-CN.md +10 -4
  3. package/lib/commonjs/Carousel.js +26 -17
  4. package/lib/commonjs/Carousel.js.map +1 -1
  5. package/lib/commonjs/ScrollViewGesture.js +34 -22
  6. package/lib/commonjs/ScrollViewGesture.js.map +1 -1
  7. package/lib/commonjs/hooks/useAutoPlay.js +27 -13
  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 +13 -9
  12. package/lib/commonjs/hooks/useInitProps.js.map +1 -1
  13. package/lib/commonjs/hooks/useOnProgressChange.js +5 -5
  14. package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
  15. package/lib/commonjs/layouts/normal.js +1 -1
  16. package/lib/commonjs/layouts/normal.js.map +1 -1
  17. package/lib/module/Carousel.js +23 -16
  18. package/lib/module/Carousel.js.map +1 -1
  19. package/lib/module/ScrollViewGesture.js +32 -22
  20. package/lib/module/ScrollViewGesture.js.map +1 -1
  21. package/lib/module/hooks/useAutoPlay.js +27 -13
  22. package/lib/module/hooks/useAutoPlay.js.map +1 -1
  23. package/lib/module/hooks/useCarouselController.js +82 -24
  24. package/lib/module/hooks/useCarouselController.js.map +1 -1
  25. package/lib/module/hooks/useInitProps.js +13 -9
  26. package/lib/module/hooks/useInitProps.js.map +1 -1
  27. package/lib/module/hooks/useOnProgressChange.js +5 -5
  28. package/lib/module/hooks/useOnProgressChange.js.map +1 -1
  29. package/lib/module/layouts/normal.js +2 -2
  30. package/lib/module/layouts/normal.js.map +1 -1
  31. package/lib/typescript/ScrollViewGesture.d.ts +3 -1
  32. package/lib/typescript/hooks/useAutoPlay.d.ts +1 -1
  33. package/lib/typescript/hooks/useCarouselController.d.ts +5 -3
  34. package/lib/typescript/hooks/useInitProps.d.ts +4 -5
  35. package/lib/typescript/hooks/useOnProgressChange.d.ts +2 -1
  36. package/lib/typescript/types.d.ts +22 -4
  37. package/package.json +5 -2
  38. package/src/Carousel.tsx +26 -15
  39. package/src/ScrollViewGesture.tsx +57 -26
  40. package/src/hooks/useAutoPlay.ts +25 -22
  41. package/src/hooks/useCarouselController.tsx +101 -42
  42. package/src/hooks/useInitProps.ts +30 -15
  43. package/src/hooks/useOnProgressChange.ts +7 -6
  44. package/src/layouts/normal.ts +2 -7
  45. package/src/types.ts +23 -4
  46. package/CHANGELOG.md +0 -353
  47. package/lib/commonjs/hooks/useIndexController.js +0 -65
  48. package/lib/commonjs/hooks/useIndexController.js.map +0 -1
  49. package/lib/module/hooks/useIndexController.js +0 -52
  50. package/lib/module/hooks/useIndexController.js.map +0 -1
  51. package/lib/typescript/hooks/useIndexController.d.ts +0 -18
  52. package/src/.DS_Store +0 -0
  53. package/src/hooks/useIndexController.ts +0 -78
@@ -1,4 +1,4 @@
1
- import { Extrapolate, interpolate } from 'react-native-reanimated';
1
+ import { interpolate } from 'react-native-reanimated';
2
2
  export function normalLayout(opts) {
3
3
  const {
4
4
  size,
@@ -7,7 +7,7 @@ export function normalLayout(opts) {
7
7
  return value => {
8
8
  'worklet';
9
9
 
10
- const translate = interpolate(value, [-1, 0, 1], [-size, 0, size], Extrapolate.CLAMP);
10
+ const translate = interpolate(value, [-1, 0, 1], [-size, 0, size]);
11
11
  return {
12
12
  transform: [vertical ? {
13
13
  translateY: translate
@@ -1 +1 @@
1
- {"version":3,"sources":["normal.ts"],"names":["Extrapolate","interpolate","normalLayout","opts","size","vertical","value","translate","CLAMP","transform","translateY","translateX"],"mappings":"AAAA,SAASA,WAAT,EAAsBC,WAAtB,QAAyC,yBAAzC;AAEA,OAAO,SAASC,YAAT,CAAsBC,IAAtB,EAAiE;AACpE,QAAM;AAAEC,IAAAA,IAAF;AAAQC,IAAAA;AAAR,MAAqBF,IAA3B;AAEA,SAAQG,KAAD,IAAmB;AACtB;;AACA,UAAMC,SAAS,GAAGN,WAAW,CACzBK,KADyB,EAEzB,CAAC,CAAC,CAAF,EAAK,CAAL,EAAQ,CAAR,CAFyB,EAGzB,CAAC,CAACF,IAAF,EAAQ,CAAR,EAAWA,IAAX,CAHyB,EAIzBJ,WAAW,CAACQ,KAJa,CAA7B;AAOA,WAAO;AACHC,MAAAA,SAAS,EAAE,CACPJ,QAAQ,GACF;AACIK,QAAAA,UAAU,EAAEH;AADhB,OADE,GAIF;AACII,QAAAA,UAAU,EAAEJ;AADhB,OALC;AADR,KAAP;AAWH,GApBD;AAqBH","sourcesContent":["import { Extrapolate, interpolate } from 'react-native-reanimated';\n\nexport function normalLayout(opts: { size: number; vertical: boolean }) {\n const { size, vertical } = opts;\n\n return (value: number) => {\n 'worklet';\n const translate = interpolate(\n value,\n [-1, 0, 1],\n [-size, 0, size],\n Extrapolate.CLAMP\n );\n\n return {\n transform: [\n vertical\n ? {\n translateY: translate,\n }\n : {\n translateX: translate,\n },\n ],\n };\n };\n}\n"]}
1
+ {"version":3,"sources":["normal.ts"],"names":["interpolate","normalLayout","opts","size","vertical","value","translate","transform","translateY","translateX"],"mappings":"AAAA,SAASA,WAAT,QAA4B,yBAA5B;AAEA,OAAO,SAASC,YAAT,CAAsBC,IAAtB,EAAiE;AACpE,QAAM;AAAEC,IAAAA,IAAF;AAAQC,IAAAA;AAAR,MAAqBF,IAA3B;AAEA,SAAQG,KAAD,IAAmB;AACtB;;AACA,UAAMC,SAAS,GAAGN,WAAW,CAACK,KAAD,EAAQ,CAAC,CAAC,CAAF,EAAK,CAAL,EAAQ,CAAR,CAAR,EAAoB,CAAC,CAACF,IAAF,EAAQ,CAAR,EAAWA,IAAX,CAApB,CAA7B;AAEA,WAAO;AACHI,MAAAA,SAAS,EAAE,CACPH,QAAQ,GACF;AACII,QAAAA,UAAU,EAAEF;AADhB,OADE,GAIF;AACIG,QAAAA,UAAU,EAAEH;AADhB,OALC;AADR,KAAP;AAWH,GAfD;AAgBH","sourcesContent":["import { interpolate } from 'react-native-reanimated';\n\nexport function normalLayout(opts: { size: number; vertical: boolean }) {\n const { size, vertical } = opts;\n\n return (value: number) => {\n 'worklet';\n const translate = interpolate(value, [-1, 0, 1], [-size, 0, size]);\n\n return {\n transform: [\n vertical\n ? {\n translateY: translate,\n }\n : {\n translateX: translate,\n },\n ],\n };\n };\n}\n"]}
@@ -4,8 +4,10 @@ import Animated from 'react-native-reanimated';
4
4
  interface Props {
5
5
  size: number;
6
6
  infinite?: boolean;
7
- onScrollEnd?: () => void;
8
7
  onScrollBegin?: () => void;
8
+ onScrollEnd?: () => void;
9
+ onTouchBegin?: () => void;
10
+ onTouchEnd?: () => void;
9
11
  style?: StyleProp<ViewStyle>;
10
12
  translation: Animated.SharedValue<number>;
11
13
  }
@@ -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,7 @@
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'];
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'> & {
4
+ rawData: T[];
7
5
  };
8
6
  export declare function useInitProps<T>(props: TCarouselProps<T>): TInitializeCarouselProps<T>;
7
+ export {};
@@ -3,4 +3,5 @@ import type { TCarouselProps } from '../types';
3
3
  export declare function useOnProgressChange(opts: {
4
4
  size: number;
5
5
  offsetX: Animated.SharedValue<number>;
6
- } & Pick<TCarouselProps, 'data' | 'onProgressChange'>): void;
6
+ rawData: TCarouselProps['data'];
7
+ } & Pick<TCarouselProps, 'onProgressChange'>): void;
@@ -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.1",
3
+ "version": "2.2.3",
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 && 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';
@@ -23,6 +25,7 @@ function Carousel<T>(
23
25
 
24
26
  const {
25
27
  data,
28
+ rawData,
26
29
  loop,
27
30
  mode,
28
31
  style,
@@ -33,6 +36,7 @@ function Carousel<T>(
33
36
  windowSize,
34
37
  autoPlayReverse,
35
38
  autoPlayInterval,
39
+ scrollAnimationDuration,
36
40
  renderItem,
37
41
  onScrollEnd,
38
42
  onSnapToItem,
@@ -55,7 +59,7 @@ function Carousel<T>(
55
59
  }, [loop, size, data]);
56
60
 
57
61
  usePropsErrorBoundary(props);
58
- useOnProgressChange({ size, offsetX, data, onProgressChange });
62
+ useOnProgressChange({ size, offsetX, rawData, onProgressChange });
59
63
 
60
64
  const carouselController = useCarouselController({
61
65
  loop,
@@ -66,8 +70,8 @@ function Carousel<T>(
66
70
  originalLength: data.length,
67
71
  onScrollEnd: () => runOnJS(_onScrollEnd)(),
68
72
  onScrollBegin: () => !!onScrollBegin && runOnJS(onScrollBegin)(),
69
- onChange: (i) => onSnapToItem && runOnJS(onSnapToItem)(i),
70
- duration: autoPlayInterval,
73
+ onChange: (i) => !!onSnapToItem && runOnJS(onSnapToItem)(i),
74
+ duration: scrollAnimationDuration,
71
75
  });
72
76
 
73
77
  const {
@@ -79,27 +83,31 @@ function Carousel<T>(
79
83
  getCurrentIndex,
80
84
  } = carouselController;
81
85
 
82
- const { run, pause } = useAutoPlay({
86
+ const { start, pause } = useAutoPlay({
83
87
  autoPlay,
84
88
  autoPlayInterval,
85
89
  autoPlayReverse,
86
90
  carouselController,
87
91
  });
88
92
 
89
- const scrollViewGestureOnScrollBegin = React.useCallback(() => {
90
- pause();
91
- onScrollBegin?.();
92
- }, [onScrollBegin, pause]);
93
-
94
93
  const _onScrollEnd = React.useCallback(() => {
95
94
  computedIndex();
96
95
  onScrollEnd?.(sharedPreIndex.current, sharedIndex.current);
97
96
  }, [sharedPreIndex, sharedIndex, computedIndex, onScrollEnd]);
98
97
 
98
+ const scrollViewGestureOnScrollBegin = React.useCallback(() => {
99
+ pause();
100
+ onScrollBegin?.();
101
+ }, [onScrollBegin, pause]);
102
+
99
103
  const scrollViewGestureOnScrollEnd = React.useCallback(() => {
100
- run();
104
+ start();
101
105
  _onScrollEnd();
102
- }, [_onScrollEnd, run]);
106
+ }, [_onScrollEnd, start]);
107
+
108
+ const scrollViewGestureOnTouchBegin = React.useCallback(pause, [pause]);
109
+
110
+ const scrollViewGestureOnTouchEnd = React.useCallback(start, [start]);
103
111
 
104
112
  const goToIndex = React.useCallback(
105
113
  (i: number, animated?: boolean) => {
@@ -115,8 +123,9 @@ function Carousel<T>(
115
123
  prev,
116
124
  getCurrentIndex,
117
125
  goToIndex,
126
+ scrollTo: carouselController.scrollTo,
118
127
  }),
119
- [getCurrentIndex, goToIndex, next, prev]
128
+ [getCurrentIndex, goToIndex, next, prev, carouselController.scrollTo]
120
129
  );
121
130
 
122
131
  const visibleRanges = useVisibleRanges({
@@ -131,11 +140,11 @@ function Carousel<T>(
131
140
  const renderLayout = React.useCallback(
132
141
  (item: T, i: number) => {
133
142
  let realIndex = i;
134
- if (data.length === DATA_LENGTH.SINGLE_ITEM) {
143
+ if (rawData.length === DATA_LENGTH.SINGLE_ITEM) {
135
144
  realIndex = i % 1;
136
145
  }
137
146
 
138
- if (data.length === DATA_LENGTH.DOUBLE_ITEM) {
147
+ if (rawData.length === DATA_LENGTH.DOUBLE_ITEM) {
139
148
  realIndex = i % 2;
140
149
  }
141
150
 
@@ -158,7 +167,7 @@ function Carousel<T>(
158
167
  );
159
168
  },
160
169
  [
161
- data,
170
+ rawData,
162
171
  offsetX,
163
172
  visibleRanges,
164
173
  renderItem,
@@ -181,6 +190,8 @@ function Carousel<T>(
181
190
  translation={handlerOffsetX}
182
191
  onScrollBegin={scrollViewGestureOnScrollBegin}
183
192
  onScrollEnd={scrollViewGestureOnScrollEnd}
193
+ onTouchBegin={scrollViewGestureOnTouchBegin}
194
+ onTouchEnd={scrollViewGestureOnTouchEnd}
184
195
  >
185
196
  <Animated.View
186
197
  key={mode}
@@ -25,8 +25,10 @@ type GestureContext = {
25
25
  interface Props {
26
26
  size: number;
27
27
  infinite?: boolean;
28
- onScrollEnd?: () => void;
29
28
  onScrollBegin?: () => void;
29
+ onScrollEnd?: () => void;
30
+ onTouchBegin?: () => void;
31
+ onTouchEnd?: () => void;
30
32
  style?: StyleProp<ViewStyle>;
31
33
  translation: Animated.SharedValue<number>;
32
34
  }
@@ -41,11 +43,18 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
41
43
  enableSnap,
42
44
  panGestureHandlerProps,
43
45
  loop: infinite,
44
- autoPlayInterval,
46
+ scrollAnimationDuration,
45
47
  },
46
48
  } = React.useContext(CTX);
47
49
 
48
- const { translation, onScrollBegin, onScrollEnd, size } = props;
50
+ const {
51
+ translation,
52
+ size,
53
+ onScrollBegin,
54
+ onScrollEnd,
55
+ onTouchBegin,
56
+ onTouchEnd,
57
+ } = props;
49
58
 
50
59
  const maxPage = data.length;
51
60
  const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
@@ -59,7 +68,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
59
68
  return withTiming(
60
69
  toValue,
61
70
  {
62
- duration: autoPlayInterval,
71
+ duration: scrollAnimationDuration,
63
72
  easing: Easing.easeOutQuart,
64
73
  },
65
74
  (isFinished) => {
@@ -69,7 +78,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
69
78
  }
70
79
  );
71
80
  },
72
- [autoPlayInterval]
81
+ [scrollAnimationDuration]
73
82
  );
74
83
 
75
84
  const endWithSpring = React.useCallback(
@@ -116,22 +125,28 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
116
125
  ]
117
126
  );
118
127
 
119
- const resetBoundary = React.useCallback(() => {
120
- 'worklet';
121
- const onFinish = (isFinished: boolean) => {
128
+ const onFinish = React.useCallback(
129
+ (isFinished: boolean) => {
130
+ 'worklet';
122
131
  if (isFinished) {
123
132
  touching.value = false;
124
133
  onScrollEnd && runOnJS(onScrollEnd)();
125
134
  }
126
- };
127
- const activeDecay = () => {
128
- touching.value = true;
129
- translation.value = withDecay(
130
- { velocity: scrollEndVelocity.value },
131
- onFinish
132
- );
133
- };
135
+ },
136
+ [onScrollEnd, touching]
137
+ );
134
138
 
139
+ const activeDecay = React.useCallback(() => {
140
+ 'worklet';
141
+ touching.value = true;
142
+ translation.value = withDecay(
143
+ { velocity: scrollEndVelocity.value },
144
+ onFinish
145
+ );
146
+ }, [onFinish, scrollEndVelocity.value, touching, translation]);
147
+
148
+ const resetBoundary = React.useCallback(() => {
149
+ 'worklet';
135
150
  if (touching.value) {
136
151
  return;
137
152
  }
@@ -158,15 +173,14 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
158
173
  }
159
174
  }
160
175
  }, [
161
- infinite,
162
- touching,
163
- _withSpring,
176
+ touching.value,
164
177
  translation,
165
- scrollEndTranslation,
166
- scrollEndVelocity,
167
- onScrollEnd,
168
178
  maxPage,
169
179
  size,
180
+ scrollEndTranslation.value,
181
+ infinite,
182
+ activeDecay,
183
+ _withSpring,
170
184
  ]);
171
185
 
172
186
  useAnimatedReaction(
@@ -176,7 +190,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
176
190
  resetBoundary();
177
191
  }
178
192
  },
179
- [pagingEnabled]
193
+ [pagingEnabled, resetBoundary]
180
194
  );
181
195
 
182
196
  const panGestureEventHandler = useAnimatedGestureHandler<
@@ -202,7 +216,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
202
216
  !infinite &&
203
217
  (translation.value > 0 || translation.value < -ctx.max)
204
218
  ) {
205
- panTranslation = panTranslation * 0.5;
219
+ const boundary = translation.value > 0 ? 0 : -ctx.max;
220
+ const fixed = boundary - ctx.panOffset;
221
+ const dynamic = panTranslation - fixed;
222
+ translation.value = boundary + dynamic * 0.5;
223
+ return;
206
224
  }
207
225
 
208
226
  translation.value = ctx.panOffset + panTranslation;
@@ -223,7 +241,16 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
223
241
  }
224
242
  },
225
243
  },
226
- [pagingEnabled, isHorizontal.value, infinite, maxPage, size, enableSnap]
244
+ [
245
+ pagingEnabled,
246
+ isHorizontal.value,
247
+ infinite,
248
+ maxPage,
249
+ size,
250
+ enableSnap,
251
+ onScrollBegin,
252
+ onScrollEnd,
253
+ ]
227
254
  );
228
255
 
229
256
  const directionStyle = React.useMemo(() => {
@@ -231,7 +258,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
231
258
  }, [vertical]);
232
259
 
233
260
  return (
234
- <Animated.View style={[styles.container, directionStyle, style]}>
261
+ <Animated.View
262
+ style={[styles.container, directionStyle, style]}
263
+ onTouchStart={onTouchBegin}
264
+ onTouchEnd={onTouchEnd}
265
+ >
235
266
  <PanGestureHandler
236
267
  {...panGestureHandlerProps}
237
268
  onGestureEvent={panGestureEventHandler}
@@ -15,44 +15,47 @@ 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();
20
+ const play = React.useCallback(() => {
21
+ if (stopped.current) {
22
+ return;
26
23
  }
27
24
 
25
+ timer.current = setTimeout(() => {
26
+ autoPlayReverse
27
+ ? carouselController.prev({ onFinished: play })
28
+ : carouselController.next({ onFinished: play });
29
+ }, autoPlayInterval);
30
+ }, [autoPlayReverse, autoPlayInterval, carouselController]);
31
+
32
+ const pause = React.useCallback(() => {
28
33
  if (!autoPlay) {
29
34
  return;
30
35
  }
36
+ timer.current && clearInterval(timer.current);
37
+ stopped.current = true;
38
+ }, [autoPlay]);
31
39
 
32
- timer.current = setInterval(() => {
33
- autoPlayReverse
34
- ? carouselController.prev()
35
- : carouselController.next();
36
- }, autoPlayInterval);
37
- }, [
38
- pause,
39
- autoPlay,
40
- autoPlayReverse,
41
- autoPlayInterval,
42
- carouselController,
43
- ]);
40
+ const start = React.useCallback(() => {
41
+ if (!autoPlay) {
42
+ return;
43
+ }
44
+ stopped.current = false;
45
+ play();
46
+ }, [play, autoPlay]);
44
47
 
45
48
  React.useEffect(() => {
46
49
  if (autoPlay) {
47
- run();
50
+ start();
48
51
  } else {
49
52
  pause();
50
53
  }
51
54
  return pause;
52
- }, [run, pause, autoPlay]);
55
+ }, [pause, start, autoPlay]);
53
56
 
54
57
  return {
55
- run,
56
58
  pause,
59
+ start,
57
60
  };
58
61
  }