react-native-reanimated-carousel 3.1.5 → 3.2.0

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 (47) hide show
  1. package/README.zh-CN.md +30 -30
  2. package/lib/commonjs/Carousel.js +1 -1
  3. package/lib/commonjs/Carousel.js.map +1 -1
  4. package/lib/commonjs/ScrollViewGesture.js +1 -1
  5. package/lib/commonjs/ScrollViewGesture.js.map +1 -1
  6. package/lib/commonjs/hooks/computeNewIndexWhenDataChanges.js +1 -1
  7. package/lib/commonjs/hooks/computeNewIndexWhenDataChanges.js.map +1 -1
  8. package/lib/commonjs/hooks/useCarouselController.js +1 -1
  9. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  10. package/lib/commonjs/hooks/useCommonVariables.js +1 -1
  11. package/lib/commonjs/hooks/useCommonVariables.js.map +1 -1
  12. package/lib/commonjs/hooks/useInitProps.js +1 -1
  13. package/lib/commonjs/hooks/useInitProps.js.map +1 -1
  14. package/lib/commonjs/hooks/useOffsetX.js +1 -1
  15. package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
  16. package/lib/commonjs/hooks/useOnProgressChange.js +1 -1
  17. package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
  18. package/lib/commonjs/hooks/useVisibleRanges.js +1 -1
  19. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  20. package/lib/commonjs/layouts/BaseLayout.js +1 -1
  21. package/lib/commonjs/layouts/BaseLayout.js.map +1 -1
  22. package/lib/commonjs/layouts/ParallaxLayout.js +1 -1
  23. package/lib/commonjs/layouts/ParallaxLayout.js.map +1 -1
  24. package/lib/commonjs/layouts/normal.js +1 -1
  25. package/lib/commonjs/layouts/normal.js.map +1 -1
  26. package/lib/commonjs/layouts/parallax.js +1 -1
  27. package/lib/commonjs/layouts/parallax.js.map +1 -1
  28. package/lib/commonjs/layouts/stack.js +1 -1
  29. package/lib/commonjs/layouts/stack.js.map +1 -1
  30. package/lib/commonjs/utils/computedWithAutoFillData.js +1 -1
  31. package/lib/commonjs/utils/computedWithAutoFillData.js.map +1 -1
  32. package/lib/commonjs/utils/dealWithAnimation.js +1 -1
  33. package/lib/commonjs/utils/dealWithAnimation.js.map +1 -1
  34. package/lib/commonjs/utils/handlerOffsetDirection.js +1 -1
  35. package/lib/commonjs/utils/log.js +1 -1
  36. package/lib/module/ScrollViewGesture.js +79 -37
  37. package/lib/module/ScrollViewGesture.js.map +1 -1
  38. package/lib/module/hooks/useInitProps.js +5 -2
  39. package/lib/module/hooks/useInitProps.js.map +1 -1
  40. package/lib/module/utils/dealWithAnimation.js +2 -6
  41. package/lib/module/utils/dealWithAnimation.js.map +1 -1
  42. package/lib/typescript/types.d.ts +10 -5
  43. package/package.json +8 -4
  44. package/src/ScrollViewGesture.tsx +80 -39
  45. package/src/hooks/useInitProps.ts +5 -2
  46. package/src/types.ts +12 -5
  47. package/src/utils/dealWithAnimation.ts +6 -10
@@ -6,9 +6,11 @@ import {
6
6
  } from "react-native-gesture-handler";
7
7
  import Animated, {
8
8
  cancelAnimation,
9
+ measure,
9
10
  runOnJS,
10
11
  useAnimatedGestureHandler,
11
12
  useAnimatedReaction,
13
+ useAnimatedRef,
12
14
  useDerivedValue,
13
15
  useSharedValue,
14
16
  withDecay,
@@ -49,6 +51,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
49
51
  withAnimation,
50
52
  enabled,
51
53
  dataLength,
54
+ overscrollEnabled,
52
55
  },
53
56
  } = React.useContext(CTX);
54
57
 
@@ -68,8 +71,27 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
68
71
  const touching = useSharedValue(false);
69
72
  const scrollEndTranslation = useSharedValue(0);
70
73
  const scrollEndVelocity = useSharedValue(0);
74
+ const containerRef = useAnimatedRef<Animated.View>();
71
75
 
72
- const _withSpring = React.useCallback(
76
+ // Get the limit of the scroll.
77
+ const getLimit = React.useCallback(() => {
78
+ "worklet";
79
+
80
+ if (!infinite && !overscrollEnabled) {
81
+ const { width: containerWidth = 0 } = measure(containerRef);
82
+
83
+ // If the item's total width is less than the container's width, then there is no need to scroll.
84
+ if (dataLength * size < containerWidth)
85
+ return 0;
86
+
87
+ // Disable the "overscroll" effect
88
+ return dataLength * size - containerWidth;
89
+ }
90
+
91
+ return dataLength * size;
92
+ }, [infinite, size, dataLength, overscrollEnabled]);
93
+
94
+ const withSpring = React.useCallback(
73
95
  (toValue: number, onFinished?: () => void) => {
74
96
  "worklet";
75
97
  const defaultWithAnimation: WithTimingAnimation = {
@@ -97,34 +119,49 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
97
119
  "worklet";
98
120
  const origin = translation.value;
99
121
  const velocity = scrollEndVelocity.value;
100
- if (!pagingEnabled) {
101
- /**
102
- * If enabled, releasing the touch will scroll to the nearest item.
103
- * valid when pagingEnabled=false
104
- */
105
- if (snapEnabled) {
106
- const nextPage
107
- = Math.round((origin + velocity * 0.4) / size) * size;
108
-
109
- translation.value = _withSpring(nextPage, onFinished);
110
- return;
122
+ // Default to scroll in the direction of the slide (with deceleration)
123
+ let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 });
124
+
125
+ /**
126
+ * The page size is the same as the item size.
127
+ * If direction is vertical, the page size is the height of the item.
128
+ * If direction is horizontal, the page size is the width of the item.
129
+ *
130
+ * `page size` equals to `size` variable.
131
+ * */
132
+ if (pagingEnabled) {
133
+ // distance with direction
134
+ const offset = -(scrollEndTranslation.value >= 0 ? 1 : -1); // 1 or -1
135
+ const computed = offset < 0 ? Math.ceil : Math.floor;
136
+ const page = computed(-translation.value / size);
137
+
138
+ if (infinite) {
139
+ const finalPage = page + offset;
140
+ finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished);
141
+ }
142
+ else {
143
+ const finalPage = Math.min(maxPage - 1, Math.max(0, page + offset));
144
+ finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished);
111
145
  }
112
- translation.value = withDecay({
113
- velocity,
114
- deceleration: 0.999,
115
- });
116
- return;
117
146
  }
118
147
 
119
- const direction = -(scrollEndTranslation.value >= 0 ? 1 : -1);
120
- const computed = direction < 0 ? Math.ceil : Math.floor;
121
- const page = computed(-translation.value / size);
122
- let finalPage = page + direction;
148
+ if (!pagingEnabled && snapEnabled) {
149
+ // scroll to the nearest item
150
+ const nextPage = Math.round((origin + velocity * 0.4) / size) * size;
151
+ finalTranslation = withSpring(withProcessTranslation(nextPage), onFinished);
152
+ }
123
153
 
124
- if (!infinite)
125
- finalPage = Math.min(maxPage - 1, Math.max(0, finalPage));
154
+ translation.value = finalTranslation;
155
+
156
+ function withProcessTranslation(translation: number) {
157
+ if (!infinite && !overscrollEnabled) {
158
+ const limit = getLimit();
159
+ const sign = Math.sign(translation);
160
+ return sign * Math.max(0, Math.min(limit, Math.abs(translation)));
161
+ }
126
162
 
127
- translation.value = _withSpring(-finalPage * size, onFinished);
163
+ return translation;
164
+ }
128
165
  },
129
166
  [
130
167
  translation,
@@ -133,7 +170,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
133
170
  size,
134
171
  scrollEndTranslation.value,
135
172
  infinite,
136
- _withSpring,
173
+ withSpring,
137
174
  snapEnabled,
138
175
  maxPage,
139
176
  ],
@@ -170,7 +207,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
170
207
  return;
171
208
  }
172
209
  if (!infinite) {
173
- translation.value = _withSpring(0);
210
+ translation.value = withSpring(0);
174
211
  return;
175
212
  }
176
213
  }
@@ -181,7 +218,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
181
218
  return;
182
219
  }
183
220
  if (!infinite)
184
- translation.value = _withSpring(-((maxPage - 1) * size));
221
+ translation.value = withSpring(-((maxPage - 1) * size));
185
222
  }
186
223
  }, [
187
224
  touching.value,
@@ -191,7 +228,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
191
228
  scrollEndTranslation.value,
192
229
  infinite,
193
230
  activeDecay,
194
- _withSpring,
231
+ withSpring,
195
232
  ]);
196
233
 
197
234
  useAnimatedReaction(
@@ -212,7 +249,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
212
249
  touching.value = true;
213
250
  ctx.validStart = true;
214
251
  onScrollBegin && runOnJS(onScrollBegin)();
252
+
215
253
  ctx.max = (maxPage - 1) * size;
254
+ if (!infinite && !overscrollEnabled)
255
+ ctx.max = getLimit();
256
+
216
257
  ctx.panOffset = translation.value;
217
258
  },
218
259
  onActive: (e, ctx) => {
@@ -225,19 +266,18 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
225
266
  const panTranslation = isHorizontal.value
226
267
  ? translationX
227
268
  : translationY;
228
-
229
- if (
230
- !infinite
231
- && (translation.value > 0 || translation.value < -ctx.max)
232
- ) {
233
- const boundary = translation.value > 0 ? 0 : -ctx.max;
234
- const fixed = boundary - ctx.panOffset;
235
- const dynamic = panTranslation - fixed;
236
- translation.value = boundary + dynamic * 0.5;
237
- return;
269
+ if (!infinite) {
270
+ if ((translation.value > 0 || translation.value < -ctx.max)) {
271
+ const boundary = translation.value > 0 ? 0 : -ctx.max;
272
+ const fixed = boundary - ctx.panOffset;
273
+ const dynamic = panTranslation - fixed;
274
+ translation.value = boundary + dynamic * 0.5;
275
+ return;
276
+ }
238
277
  }
239
278
 
240
- translation.value = ctx.panOffset + panTranslation;
279
+ const translationValue = ctx.panOffset + panTranslation;
280
+ translation.value = translationValue;
241
281
  },
242
282
  onEnd: (e) => {
243
283
  const { velocityX, velocityY, translationX, translationY } = e;
@@ -273,6 +313,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
273
313
  onGestureEvent={panGestureEventHandler}
274
314
  >
275
315
  <Animated.View
316
+ ref={containerRef}
276
317
  testID={testID}
277
318
  style={style}
278
319
  onTouchStart={onTouchBegin}
@@ -31,13 +31,15 @@ export function useInitProps<T>(
31
31
  defaultIndex = 0,
32
32
  data: rawData = [],
33
33
  loop = true,
34
- enabled = true,
35
34
  autoPlayInterval: _autoPlayInterval = 1000,
36
35
  scrollAnimationDuration = 500,
37
36
  style = {},
38
37
  panGestureHandlerProps = {},
39
- pagingEnabled = true,
40
38
  autoFillData = true,
39
+ // switchers
40
+ enabled = true,
41
+ pagingEnabled = true,
42
+ overscrollEnabled = true,
41
43
  snapEnabled = props.enableSnap ?? true,
42
44
  width: _width,
43
45
  height: _height,
@@ -89,6 +91,7 @@ export function useInitProps<T>(
89
91
  panGestureHandlerProps,
90
92
  pagingEnabled,
91
93
  snapEnabled,
94
+ overscrollEnabled,
92
95
  width,
93
96
  height,
94
97
  };
package/src/types.ts CHANGED
@@ -132,15 +132,14 @@ export type TCarouselProps<T = any> = {
132
132
  /**
133
133
  * If enabled, releasing the touch will scroll to the nearest item.
134
134
  * valid when pagingEnabled=false
135
- * @deprecated please use snapEnabled instead
135
+ * @default true
136
136
  */
137
- enableSnap?: boolean
137
+ snapEnabled?: boolean
138
138
  /**
139
- * If enabled, releasing the touch will scroll to the nearest item.
140
- * valid when pagingEnabled=false
139
+ * If enabled, items will scroll to the first placement when scrolling past the edge rather than closing to the last. (previous conditions: loop=false)
141
140
  * @default true
142
141
  */
143
- snapEnabled?: boolean
142
+ overscrollEnabled?: boolean
144
143
  /**
145
144
  * If false, Carousel will not respond to any gestures.
146
145
  * @default true
@@ -188,6 +187,14 @@ export type TCarouselProps<T = any> = {
188
187
  offsetProgress: number,
189
188
  absoluteProgress: number
190
189
  ) => void
190
+
191
+ // ============================== deprecated props ==============================
192
+ /**
193
+ * If enabled, releasing the touch will scroll to the nearest item.
194
+ * valid when pagingEnabled=false
195
+ * @deprecated please use snapEnabled instead
196
+ */
197
+ enableSnap?: boolean
191
198
  } & (TParallaxModeProps | TStackModeProps);
192
199
 
193
200
  export interface ICarouselInstance {
@@ -8,16 +8,12 @@ export function dealWithAnimation(
8
8
  "worklet";
9
9
  switch (withAnimation.type) {
10
10
  case "spring":
11
- return (value, cb) => {
12
- return withSpring(value, withAnimation.config, isFinished =>
13
- cb(isFinished as boolean),
14
- );
15
- };
11
+ return (value, cb) => withSpring(value, withAnimation.config, isFinished =>
12
+ cb(isFinished as boolean),
13
+ );
16
14
  case "timing":
17
- return (value, cb) => {
18
- return withTiming(value, withAnimation.config, isFinished =>
19
- cb(isFinished as boolean),
20
- );
21
- };
15
+ return (value, cb) => withTiming(value, withAnimation.config, isFinished =>
16
+ cb(isFinished as boolean),
17
+ );
22
18
  }
23
19
  }