react-native-reanimated-carousel 5.1.1 → 5.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 (141) hide show
  1. package/README.md +18 -1
  2. package/lib/commonjs/components/CarouselLayout.js +1 -1
  3. package/lib/commonjs/components/CarouselLayout.js.map +1 -1
  4. package/lib/commonjs/components/ItemLayout.js +1 -1
  5. package/lib/commonjs/components/ItemLayout.js.map +1 -1
  6. package/lib/commonjs/components/ItemRenderer.js +1 -1
  7. package/lib/commonjs/components/ItemRenderer.js.map +1 -1
  8. package/lib/commonjs/components/Pagination/PaginationItem.js +1 -1
  9. package/lib/commonjs/components/Pagination/PaginationItem.js.map +1 -1
  10. package/lib/commonjs/components/ScrollViewGesture.js +1 -1
  11. package/lib/commonjs/components/ScrollViewGesture.js.map +1 -1
  12. package/lib/commonjs/constants/index.js +1 -1
  13. package/lib/commonjs/constants/index.js.map +1 -1
  14. package/lib/commonjs/hooks/useAutoPlay.js +1 -1
  15. package/lib/commonjs/hooks/useAutoPlay.js.map +1 -1
  16. package/lib/commonjs/hooks/useCarouselController.js +1 -1
  17. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  18. package/lib/commonjs/hooks/useCommonVariables.js +1 -1
  19. package/lib/commonjs/hooks/useCommonVariables.js.map +1 -1
  20. package/lib/commonjs/hooks/useInitProps.js +1 -1
  21. package/lib/commonjs/hooks/useInitProps.js.map +1 -1
  22. package/lib/commonjs/hooks/useOffsetX.js +1 -1
  23. package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
  24. package/lib/commonjs/hooks/useOnProgressChange.js +1 -1
  25. package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
  26. package/lib/commonjs/hooks/useVisibleRanges.js +1 -1
  27. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  28. package/lib/commonjs/layouts/normal.js +1 -1
  29. package/lib/commonjs/layouts/normal.js.map +1 -1
  30. package/lib/commonjs/layouts/parallax.js +1 -1
  31. package/lib/commonjs/layouts/parallax.js.map +1 -1
  32. package/lib/commonjs/utils/web-only-dependencies.js +2 -0
  33. package/lib/commonjs/utils/web-only-dependencies.js.map +1 -0
  34. package/lib/module/components/CarouselLayout.js +1 -1
  35. package/lib/module/components/CarouselLayout.js.map +1 -1
  36. package/lib/module/components/ItemLayout.js +1 -1
  37. package/lib/module/components/ItemLayout.js.map +1 -1
  38. package/lib/module/components/ItemRenderer.js +1 -1
  39. package/lib/module/components/ItemRenderer.js.map +1 -1
  40. package/lib/module/components/Pagination/PaginationItem.js +1 -1
  41. package/lib/module/components/Pagination/PaginationItem.js.map +1 -1
  42. package/lib/module/components/ScrollViewGesture.js +1 -1
  43. package/lib/module/components/ScrollViewGesture.js.map +1 -1
  44. package/lib/module/constants/index.js +1 -1
  45. package/lib/module/constants/index.js.map +1 -1
  46. package/lib/module/hooks/useAutoPlay.js +1 -1
  47. package/lib/module/hooks/useAutoPlay.js.map +1 -1
  48. package/lib/module/hooks/useCarouselController.js +1 -1
  49. package/lib/module/hooks/useCarouselController.js.map +1 -1
  50. package/lib/module/hooks/useCommonVariables.js +1 -1
  51. package/lib/module/hooks/useCommonVariables.js.map +1 -1
  52. package/lib/module/hooks/useInitProps.js +1 -1
  53. package/lib/module/hooks/useInitProps.js.map +1 -1
  54. package/lib/module/hooks/useOffsetX.js +1 -1
  55. package/lib/module/hooks/useOffsetX.js.map +1 -1
  56. package/lib/module/hooks/useOnProgressChange.js +1 -1
  57. package/lib/module/hooks/useOnProgressChange.js.map +1 -1
  58. package/lib/module/hooks/useVisibleRanges.js +1 -1
  59. package/lib/module/hooks/useVisibleRanges.js.map +1 -1
  60. package/lib/module/layouts/normal.js +1 -1
  61. package/lib/module/layouts/normal.js.map +1 -1
  62. package/lib/module/layouts/parallax.js +1 -1
  63. package/lib/module/layouts/parallax.js.map +1 -1
  64. package/lib/module/utils/web-only-dependencies.js +2 -0
  65. package/lib/module/utils/web-only-dependencies.js.map +1 -0
  66. package/lib/typescript/commonjs/components/CarouselLayout.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/components/ItemLayout.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/components/ItemRenderer.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/components/Pagination/PaginationItem.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/components/ScrollViewGesture.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/constants/index.d.ts +4 -4
  72. package/lib/typescript/commonjs/constants/index.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/hooks/useAutoPlay.d.ts +19 -5
  74. package/lib/typescript/commonjs/hooks/useAutoPlay.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/hooks/useCarouselController.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/hooks/useCommonVariables.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/hooks/useInitProps.d.ts +2 -1
  78. package/lib/typescript/commonjs/hooks/useInitProps.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/hooks/useOffsetX.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/hooks/useOnProgressChange.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/hooks/usePanGestureProxy.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/hooks/useUpdateGestureConfig.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/hooks/useVisibleRanges.d.ts +1 -0
  84. package/lib/typescript/commonjs/hooks/useVisibleRanges.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/layouts/normal.d.ts +2 -9
  86. package/lib/typescript/commonjs/layouts/normal.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/layouts/parallax.d.ts +2 -16
  88. package/lib/typescript/commonjs/layouts/parallax.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/layouts/stack.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/public-types.d.ts +1 -0
  91. package/lib/typescript/commonjs/public-types.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/store/index.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/utils/web-only-dependencies.d.ts +7 -0
  94. package/lib/typescript/commonjs/utils/web-only-dependencies.d.ts.map +1 -0
  95. package/lib/typescript/module/components/CarouselLayout.d.ts.map +1 -1
  96. package/lib/typescript/module/components/ItemLayout.d.ts.map +1 -1
  97. package/lib/typescript/module/components/ItemRenderer.d.ts.map +1 -1
  98. package/lib/typescript/module/components/Pagination/PaginationItem.d.ts.map +1 -1
  99. package/lib/typescript/module/components/ScrollViewGesture.d.ts.map +1 -1
  100. package/lib/typescript/module/constants/index.d.ts +4 -4
  101. package/lib/typescript/module/constants/index.d.ts.map +1 -1
  102. package/lib/typescript/module/hooks/useAutoPlay.d.ts +19 -5
  103. package/lib/typescript/module/hooks/useAutoPlay.d.ts.map +1 -1
  104. package/lib/typescript/module/hooks/useCarouselController.d.ts.map +1 -1
  105. package/lib/typescript/module/hooks/useCommonVariables.d.ts.map +1 -1
  106. package/lib/typescript/module/hooks/useInitProps.d.ts +2 -1
  107. package/lib/typescript/module/hooks/useInitProps.d.ts.map +1 -1
  108. package/lib/typescript/module/hooks/useOffsetX.d.ts.map +1 -1
  109. package/lib/typescript/module/hooks/useOnProgressChange.d.ts.map +1 -1
  110. package/lib/typescript/module/hooks/usePanGestureProxy.d.ts.map +1 -1
  111. package/lib/typescript/module/hooks/useUpdateGestureConfig.d.ts.map +1 -1
  112. package/lib/typescript/module/hooks/useVisibleRanges.d.ts +1 -0
  113. package/lib/typescript/module/hooks/useVisibleRanges.d.ts.map +1 -1
  114. package/lib/typescript/module/layouts/normal.d.ts +2 -9
  115. package/lib/typescript/module/layouts/normal.d.ts.map +1 -1
  116. package/lib/typescript/module/layouts/parallax.d.ts +2 -16
  117. package/lib/typescript/module/layouts/parallax.d.ts.map +1 -1
  118. package/lib/typescript/module/layouts/stack.d.ts.map +1 -1
  119. package/lib/typescript/module/public-types.d.ts +1 -0
  120. package/lib/typescript/module/public-types.d.ts.map +1 -1
  121. package/lib/typescript/module/store/index.d.ts.map +1 -1
  122. package/lib/typescript/module/utils/web-only-dependencies.d.ts +7 -0
  123. package/lib/typescript/module/utils/web-only-dependencies.d.ts.map +1 -0
  124. package/package.json +8 -4
  125. package/src/components/CarouselLayout.tsx +42 -12
  126. package/src/components/ItemLayout.tsx +14 -4
  127. package/src/components/ItemRenderer.tsx +36 -4
  128. package/src/components/Pagination/PaginationItem.tsx +22 -20
  129. package/src/components/ScrollViewGesture.tsx +12 -5
  130. package/src/constants/index.ts +4 -4
  131. package/src/hooks/useAutoPlay.ts +44 -17
  132. package/src/hooks/useCarouselController.tsx +3 -2
  133. package/src/hooks/useCommonVariables.ts +3 -2
  134. package/src/hooks/useInitProps.ts +13 -0
  135. package/src/hooks/useOffsetX.ts +11 -1
  136. package/src/hooks/useOnProgressChange.ts +2 -1
  137. package/src/hooks/useVisibleRanges.tsx +5 -2
  138. package/src/layouts/normal.ts +2 -1
  139. package/src/layouts/parallax.ts +2 -1
  140. package/src/public-types.ts +1 -0
  141. package/src/utils/web-only-dependencies.ts +10 -0
@@ -21,6 +21,7 @@ import { useGlobalState } from "../store";
21
21
  import { toLogicalGestureValue } from "../utils/carousel-direction";
22
22
  import { computeGestureTranslation } from "../utils/compute-gesture-translation";
23
23
  import { dealWithAnimation } from "../utils/deal-with-animation";
24
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
24
25
 
25
26
  interface Props {
26
27
  size: number;
@@ -40,6 +41,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
40
41
  onConfigurePanGesture,
41
42
  orientation,
42
43
  snapMode,
44
+ snapMomentum,
43
45
  loop,
44
46
  animation,
45
47
  scrollEnabled,
@@ -63,7 +65,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
63
65
  const isVertical = orientation === "vertical";
64
66
 
65
67
  const maxPage = dataLength;
66
- const isHorizontal = useDerivedValue(() => !isVertical, [isVertical]);
68
+ const isHorizontal = useDerivedValue(() => !isVertical, webOnlyDependencies([isVertical]));
67
69
  const max = useSharedValue(0);
68
70
  const panOffset = useSharedValue<number | undefined>(undefined); // set to undefined when not actively in a pan gesture
69
71
  const touching = useSharedValue(false);
@@ -71,10 +73,11 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
71
73
  const hasStartedMovement = useSharedValue(false);
72
74
  const scrollEndTranslation = useSharedValue(0);
73
75
  const scrollEndVelocity = useSharedValue(0);
76
+ const sizeReadyDependencies = webOnlyDependencies([resolvedSize, sizePhase]);
74
77
  const sizeReady = useDerivedValue(() => {
75
78
  const currentSize = resolvedSize.value ?? 0;
76
79
  return sizePhase.value === "ready" && currentSize > 0;
77
- }, [resolvedSize, sizePhase]);
80
+ }, sizeReadyDependencies);
78
81
 
79
82
  // Get the limit of the scroll.
80
83
  const getLimit = React.useCallback(() => {
@@ -169,7 +172,11 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
169
172
  }
170
173
 
171
174
  if (snapMode === "nearest") {
172
- finalTranslation = withSpring(withProcessTranslation(-nextPage * currentSize), onFinished);
175
+ const nearestPage = -Math.round((origin + velocity * snapMomentum) / currentSize);
176
+ finalTranslation = withSpring(
177
+ withProcessTranslation(-nearestPage * currentSize),
178
+ onFinished
179
+ );
173
180
  }
174
181
 
175
182
  translation.value = finalTranslation;
@@ -183,7 +190,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
183
190
  return translation;
184
191
  }
185
192
  },
186
- [withSpring, resolvedSize, maxPage, loop, snapMode, translation]
193
+ [withSpring, resolvedSize, maxPage, loop, snapMode, snapMomentum, translation]
187
194
  );
188
195
 
189
196
  const onFinish = React.useCallback(
@@ -238,7 +245,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
238
245
  () => {
239
246
  if (snapMode !== "page") resetBoundary();
240
247
  },
241
- [resetBoundary, snapMode]
248
+ webOnlyDependencies([resetBoundary, snapMode])
242
249
  );
243
250
 
244
251
  const onGestureStart = useCallback(
@@ -1,10 +1,10 @@
1
1
  import type { EasingFunction } from "react-native-reanimated";
2
2
  import { Easing as _Easing } from "react-native-reanimated";
3
3
 
4
- export enum DATA_LENGTH {
5
- SINGLE_ITEM = 1,
6
- DOUBLE_ITEM = 2,
7
- }
4
+ export const DATA_LENGTH = {
5
+ SINGLE_ITEM: 1,
6
+ DOUBLE_ITEM: 2,
7
+ } as const;
8
8
 
9
9
  export const Easing = {
10
10
  easeOutQuart: _Easing.bezier(0.25, 1, 0.5, 1) as unknown as EasingFunction,
@@ -2,12 +2,31 @@ import * as React from "react";
2
2
 
3
3
  import type { CarouselController } from "./useCarouselController";
4
4
 
5
+ export interface AutoPlayControls {
6
+ /** Stops autoplay until `start` is called, for example while the user touches the carousel. */
7
+ pause: () => void;
8
+ /** Resumes autoplay and begins a fresh dwell. */
9
+ start: () => void;
10
+ /** Begins a fresh dwell if autoplay is running. */
11
+ trigger: () => void;
12
+ /** A transition started from any source: drop the pending dwell without stopping autoplay. */
13
+ onTransitionStart: () => void;
14
+ /** A transition settled from any source: begin a fresh dwell if autoplay is running. */
15
+ onTransitionEnd: () => void;
16
+ }
17
+
18
+ /**
19
+ * Autoplay waits `autoplayInterval` of idle time, then steps once. The dwell is restarted
20
+ * when a transition settles, whatever started it (an autoplay step, `ref.next/prev/scrollTo`,
21
+ * or a pagination tap), so a manual move never cuts the next dwell short and an interrupted
22
+ * autoplay step does not stop autoplay.
23
+ */
5
24
  export function useAutoPlay(options: {
6
25
  autoplay?: boolean;
7
26
  autoplayInterval?: number;
8
27
  autoplayDirection?: "forward" | "backward";
9
28
  carouselController: CarouselController;
10
- }) {
29
+ }): AutoPlayControls {
11
30
  const {
12
31
  autoplay = false,
13
32
  autoplayDirection = "forward",
@@ -18,28 +37,31 @@ export function useAutoPlay(options: {
18
37
  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
19
38
  const stopped = React.useRef(!autoplay);
20
39
 
21
- const play = React.useCallback(() => {
22
- if (stopped.current) return;
23
-
40
+ const clearDwell = React.useCallback(() => {
24
41
  if (timer.current) {
25
42
  clearTimeout(timer.current);
26
43
  timer.current = null;
27
44
  }
45
+ }, []);
46
+
47
+ const play = React.useCallback(() => {
48
+ if (stopped.current) return;
49
+
50
+ clearDwell();
28
51
  timer.current = setTimeout(() => {
29
- if (autoplayDirection === "backward") prev({ onFinished: play });
30
- else next({ onFinished: play });
52
+ timer.current = null;
53
+ // The step settles through `onTransitionEnd`, which starts the next dwell.
54
+ if (autoplayDirection === "backward") prev();
55
+ else next();
31
56
  }, autoplayInterval);
32
- }, [autoplayDirection, autoplayInterval, next, prev]);
57
+ }, [autoplayDirection, autoplayInterval, clearDwell, next, prev]);
33
58
 
34
59
  const pause = React.useCallback(() => {
35
60
  if (!autoplay) return;
36
61
 
37
- if (timer.current) {
38
- clearTimeout(timer.current);
39
- timer.current = null;
40
- }
62
+ clearDwell();
41
63
  stopped.current = true;
42
- }, [autoplay]);
64
+ }, [autoplay, clearDwell]);
43
65
 
44
66
  const start = React.useCallback(() => {
45
67
  if (!autoplay) return;
@@ -55,9 +77,14 @@ export function useAutoPlay(options: {
55
77
  return pause;
56
78
  }, [autoplay, pause, start]);
57
79
 
58
- return {
59
- pause,
60
- start,
61
- trigger: play,
62
- };
80
+ return React.useMemo(
81
+ () => ({
82
+ pause,
83
+ start,
84
+ trigger: play,
85
+ onTransitionStart: clearDwell,
86
+ onTransitionEnd: play,
87
+ }),
88
+ [clearDwell, pause, play, start]
89
+ );
63
90
  }
@@ -18,6 +18,7 @@ import {
18
18
  positiveModulo,
19
19
  } from "../utils/carousel-math";
20
20
  import { dealWithAnimation } from "../utils/deal-with-animation";
21
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
21
22
 
22
23
  interface CarouselControllerOptions {
23
24
  ref: React.ForwardedRef<CarouselRef>;
@@ -71,7 +72,7 @@ export function useCarouselController(options: CarouselControllerOptions): Carou
71
72
  const liveRawIndex = useRef(rawDataLength > 0 ? defaultIndex : 0);
72
73
  const settledIndex = useRef(rawDataLength > 0 ? defaultIndex : 0);
73
74
  const warnedCommands = useRef(new Set<string>());
74
- const canMove = rawDataLength > 1;
75
+ const canMove = rawDataLength > 1 || (loop && rawDataLength === 1 && dataLength > 1);
75
76
 
76
77
  const warnOnce = React.useCallback((key: string, message: string) => {
77
78
  if (!__DEV__ || warnedCommands.current.has(key)) return;
@@ -106,7 +107,7 @@ export function useCarouselController(options: CarouselControllerOptions): Carou
106
107
  index.value = physicalIndex;
107
108
  scheduleOnRN(setLiveRawIndex, rawIndex);
108
109
  },
109
- [dataLength, handlerOffset, index, rawDataLength, size]
110
+ webOnlyDependencies([dataLength, handlerOffset, index, rawDataLength, size])
110
111
  );
111
112
 
112
113
  const settle = React.useCallback(
@@ -12,6 +12,7 @@ import {
12
12
  reconcileOffsetAfterDataChange,
13
13
  } from "../utils/carousel-math";
14
14
  import { computeOffsetIfSizeChanged } from "../utils/compute-offset-if-size-changed";
15
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
15
16
 
16
17
  export type CarouselSizePhase = "pending" | "ready" | "updating";
17
18
 
@@ -97,7 +98,7 @@ export function useCommonVariables(
97
98
  scheduleOnRN(syncSize, current);
98
99
  }
99
100
  },
100
- [syncSize]
101
+ webOnlyDependencies([syncSize])
101
102
  );
102
103
 
103
104
  React.useEffect(() => {
@@ -242,7 +243,7 @@ export function useCommonVariables(
242
243
  prevSize.value = currentSize;
243
244
  sizePhase.value = "ready";
244
245
  },
245
- [dataLength, defaultIndex, hasInitializedOffset, resolvedSize, sizePhase]
246
+ webOnlyDependencies([dataLength, defaultIndex, hasInitializedOffset, resolvedSize, sizePhase])
246
247
  );
247
248
 
248
249
  return {
@@ -20,6 +20,7 @@ export type InitializedCarouselProps<Item> = Omit<
20
20
  | "overscrollEnabled"
21
21
  | "scrollEnabled"
22
22
  | "snapMode"
23
+ | "snapMomentum"
23
24
  | "style"
24
25
  > & {
25
26
  autoplay: boolean;
@@ -32,6 +33,7 @@ export type InitializedCarouselProps<Item> = Omit<
32
33
  overscrollEnabled: boolean;
33
34
  scrollEnabled: boolean;
34
35
  snapMode: CarouselSnapMode;
36
+ snapMomentum: number;
35
37
  style: NonNullable<CarouselProps<Item>["style"]>;
36
38
  animation: CarouselAnimation;
37
39
  rawData: Item[];
@@ -57,6 +59,7 @@ export function useInitProps<Item>(props: CarouselProps<Item>): InitializedCarou
57
59
  });
58
60
  const scrollEnabled = props.scrollEnabled ?? true;
59
61
  const snapMode = props.snapMode ?? "page";
62
+ const snapMomentum = props.snapMomentum ?? 2;
60
63
  const overscrollEnabled = props.overscrollEnabled ?? true;
61
64
  const style = props.style ?? {};
62
65
  const animation: CarouselAnimation = props.animation ?? {
@@ -123,6 +126,7 @@ export function useInitProps<Item>(props: CarouselProps<Item>): InitializedCarou
123
126
  overscrollEnabled,
124
127
  scrollEnabled,
125
128
  snapMode,
129
+ snapMomentum,
126
130
  style,
127
131
  animation,
128
132
  data,
@@ -154,6 +158,15 @@ function validateStaticProps<Item>(props: CarouselProps<Item>) {
154
158
  );
155
159
  }
156
160
 
161
+ if (
162
+ props.snapMomentum !== undefined &&
163
+ (!Number.isFinite(props.snapMomentum) || props.snapMomentum < 0)
164
+ ) {
165
+ throw new Error(
166
+ "[react-native-reanimated-carousel] snapMomentum must be a non-negative number."
167
+ );
168
+ }
169
+
157
170
  if (
158
171
  props.renderWindowSize !== undefined &&
159
172
  (!Number.isInteger(props.renderWindowSize) || props.renderWindowSize <= 0)
@@ -1,6 +1,7 @@
1
1
  import type { SharedValue } from "react-native-reanimated";
2
2
  import { Extrapolation, interpolate, useDerivedValue } from "react-native-reanimated";
3
3
 
4
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
4
5
  import type { VisibleRangesValue } from "./useVisibleRanges";
5
6
 
6
7
  export interface OffsetOptions {
@@ -38,6 +39,15 @@ export const useOffsetX = (opts: OffsetOptions, visibleRanges: VisibleRangesValu
38
39
  const MAX = positiveCount * size;
39
40
  const MIN = -((VALID_LENGTH - positiveCount) * size);
40
41
 
42
+ const xDependencies = webOnlyDependencies([
43
+ loop,
44
+ dataLength,
45
+ viewCount,
46
+ type,
47
+ size,
48
+ visibleRanges,
49
+ handlerOffset,
50
+ ]);
41
51
  const x = useDerivedValue(() => {
42
52
  if (!size) return 0;
43
53
 
@@ -75,7 +85,7 @@ export const useOffsetX = (opts: OffsetOptions, visibleRanges: VisibleRangesValu
75
85
  }
76
86
 
77
87
  return Number.MAX_SAFE_INTEGER;
78
- }, [loop, dataLength, viewCount, type, size, visibleRanges, handlerOffset]);
88
+ }, xDependencies);
79
89
 
80
90
  return x;
81
91
  };
@@ -4,6 +4,7 @@ import { scheduleOnRN } from "react-native-worklets";
4
4
 
5
5
  import type { CarouselProgressChangeHandler } from "../types";
6
6
  import { getLogicalProgress } from "../utils/carousel-math";
7
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
7
8
 
8
9
  export function useOnProgressChange(options: {
9
10
  loop: boolean;
@@ -28,6 +29,6 @@ export function useOnProgressChange(options: {
28
29
  if (progress) progress.value = nextProgress;
29
30
  if (onProgressChange) scheduleOnRN(onProgressChange, nextProgress);
30
31
  },
31
- [loop, offset, onProgressChange, progress, rawDataLength, size, sizeReady]
32
+ webOnlyDependencies([loop, offset, onProgressChange, progress, rawDataLength, size, sizeReady])
32
33
  );
33
34
  }
@@ -2,6 +2,8 @@ import { useRef } from "react";
2
2
  import type { SharedValue } from "react-native-reanimated";
3
3
  import { useDerivedValue } from "react-native-reanimated";
4
4
 
5
+ import { webOnlyDependencies } from "../utils/web-only-dependencies";
6
+
5
7
  type Range = [number, number];
6
8
 
7
9
  export interface VisibleRanges {
@@ -11,7 +13,7 @@ export interface VisibleRanges {
11
13
 
12
14
  export type VisibleRangesValue = SharedValue<VisibleRanges>;
13
15
 
14
- function normalizeWindowSize(total: number, windowSize?: number) {
16
+ export function normalizeWindowSize(total: number, windowSize?: number) {
15
17
  "worklet";
16
18
 
17
19
  return typeof windowSize === "number" && Number.isFinite(windowSize) && windowSize > 0
@@ -100,6 +102,7 @@ export function useVisibleRanges(options: {
100
102
  const windowSize = normalizeWindowSize(total, _windowSize);
101
103
  const cachedRanges = useRef<VisibleRanges | null>(null);
102
104
 
105
+ const rangesDependencies = webOnlyDependencies([loop, total, windowSize, translation, viewSize]);
103
106
  const ranges = useDerivedValue(() => {
104
107
  if (total <= 0) {
105
108
  return computeVisibleRanges({ total, currentIndex: 0, windowSize, loop });
@@ -128,7 +131,7 @@ export function useVisibleRanges(options: {
128
131
 
129
132
  cachedRanges.current = newRanges;
130
133
  return newRanges;
131
- }, [loop, total, windowSize, translation, viewSize]);
134
+ }, rangesDependencies);
132
135
 
133
136
  return ranges;
134
137
  }
@@ -1,3 +1,4 @@
1
+ import type { ViewStyle } from "react-native";
1
2
  import { interpolate } from "react-native-reanimated";
2
3
 
3
4
  import type { CarouselDirectionSign } from "../utils/carousel-direction";
@@ -10,7 +11,7 @@ export function normalLayout(opts: {
10
11
  }) {
11
12
  const { size, isVertical, directionSign } = opts;
12
13
 
13
- return (value: number) => {
14
+ return (value: number): ViewStyle => {
14
15
  "worklet";
15
16
  const logicalTranslate = interpolate(value, [-1, 0, 1], [-size, 0, size]);
16
17
  const translate = isVertical
@@ -1,3 +1,4 @@
1
+ import type { ViewStyle } from "react-native";
1
2
  import { Extrapolation, interpolate } from "react-native-reanimated";
2
3
 
3
4
  import type { CarouselLayout } from "../types";
@@ -17,7 +18,7 @@ export function parallaxLayout(
17
18
  const { size, isVertical, directionSign } = baseConfig;
18
19
  const { offset = 100, scale = 0.8, adjacentScale = scale ** 2 } = config;
19
20
 
20
- return (relativeProgress: number) => {
21
+ return (relativeProgress: number): ViewStyle => {
21
22
  "worklet";
22
23
  const logicalTranslate = interpolate(
23
24
  relativeProgress,
@@ -76,6 +76,7 @@ export type CarouselProps<Item> = {
76
76
  itemSize?: number;
77
77
  scrollEnabled?: boolean;
78
78
  snapMode?: CarouselSnapMode;
79
+ snapMomentum?: number;
79
80
  overscrollEnabled?: boolean;
80
81
  renderWindowSize?: number;
81
82
  animation?: CarouselAnimation;
@@ -0,0 +1,10 @@
1
+ import { Platform } from "react-native";
2
+
3
+ /**
4
+ * Reanimated only reads hook dependency arrays in its Web implementation when the
5
+ * worklets Babel plugin is not used. Native derives them from the worklet closure
6
+ * and, since Reanimated 4.6, warns on every render when an array is passed.
7
+ */
8
+ export function webOnlyDependencies<T extends unknown[]>(dependencies: T): T | undefined {
9
+ return Platform.OS === "web" ? dependencies : undefined;
10
+ }