react-native-reanimated-carousel 5.1.0 → 5.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.
- package/README.md +6 -0
- package/lib/commonjs/components/CarouselLayout.js +1 -1
- package/lib/commonjs/components/CarouselLayout.js.map +1 -1
- package/lib/commonjs/components/ItemLayout.js +1 -1
- package/lib/commonjs/components/ItemLayout.js.map +1 -1
- package/lib/commonjs/components/ItemRenderer.js +1 -1
- package/lib/commonjs/components/ItemRenderer.js.map +1 -1
- package/lib/commonjs/components/Pagination/PaginationItem.js +1 -1
- package/lib/commonjs/components/Pagination/PaginationItem.js.map +1 -1
- package/lib/commonjs/components/ScrollViewGesture.js +1 -1
- package/lib/commonjs/components/ScrollViewGesture.js.map +1 -1
- package/lib/commonjs/hooks/useCarouselController.js +1 -1
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
- package/lib/commonjs/hooks/useCommonVariables.js +1 -1
- package/lib/commonjs/hooks/useCommonVariables.js.map +1 -1
- package/lib/commonjs/hooks/useInitProps.js +1 -1
- package/lib/commonjs/hooks/useInitProps.js.map +1 -1
- package/lib/commonjs/hooks/useOffsetX.js +1 -1
- package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
- package/lib/commonjs/hooks/useOnProgressChange.js +1 -1
- package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
- package/lib/commonjs/layouts/normal.js +1 -1
- package/lib/commonjs/layouts/normal.js.map +1 -1
- package/lib/commonjs/layouts/parallax.js +1 -1
- package/lib/commonjs/layouts/parallax.js.map +1 -1
- package/lib/commonjs/store/index.js.map +1 -1
- package/lib/commonjs/utils/web-only-dependencies.js +2 -0
- package/lib/commonjs/utils/web-only-dependencies.js.map +1 -0
- package/lib/module/components/CarouselLayout.js +1 -1
- package/lib/module/components/CarouselLayout.js.map +1 -1
- package/lib/module/components/ItemLayout.js +1 -1
- package/lib/module/components/ItemLayout.js.map +1 -1
- package/lib/module/components/ItemRenderer.js +1 -1
- package/lib/module/components/ItemRenderer.js.map +1 -1
- package/lib/module/components/Pagination/PaginationItem.js +1 -1
- package/lib/module/components/Pagination/PaginationItem.js.map +1 -1
- package/lib/module/components/ScrollViewGesture.js +1 -1
- package/lib/module/components/ScrollViewGesture.js.map +1 -1
- package/lib/module/hooks/useCarouselController.js +1 -1
- package/lib/module/hooks/useCarouselController.js.map +1 -1
- package/lib/module/hooks/useCommonVariables.js +1 -1
- package/lib/module/hooks/useCommonVariables.js.map +1 -1
- package/lib/module/hooks/useInitProps.js +1 -1
- package/lib/module/hooks/useInitProps.js.map +1 -1
- package/lib/module/hooks/useOffsetX.js +1 -1
- package/lib/module/hooks/useOffsetX.js.map +1 -1
- package/lib/module/hooks/useOnProgressChange.js +1 -1
- package/lib/module/hooks/useOnProgressChange.js.map +1 -1
- package/lib/module/hooks/useVisibleRanges.js +1 -1
- package/lib/module/hooks/useVisibleRanges.js.map +1 -1
- package/lib/module/layouts/normal.js +1 -1
- package/lib/module/layouts/normal.js.map +1 -1
- package/lib/module/layouts/parallax.js +1 -1
- package/lib/module/layouts/parallax.js.map +1 -1
- package/lib/module/store/index.js.map +1 -1
- package/lib/module/utils/web-only-dependencies.js +2 -0
- package/lib/module/utils/web-only-dependencies.js.map +1 -0
- package/lib/typescript/commonjs/components/CarouselLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/ItemLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/ItemRenderer.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Pagination/PaginationItem.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/ScrollViewGesture.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useCarouselController.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useCommonVariables.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useInitProps.d.ts +2 -1
- package/lib/typescript/commonjs/hooks/useInitProps.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useOffsetX.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useOnProgressChange.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/useVisibleRanges.d.ts +1 -0
- package/lib/typescript/commonjs/hooks/useVisibleRanges.d.ts.map +1 -1
- package/lib/typescript/commonjs/layouts/normal.d.ts +2 -9
- package/lib/typescript/commonjs/layouts/normal.d.ts.map +1 -1
- package/lib/typescript/commonjs/layouts/parallax.d.ts +2 -16
- package/lib/typescript/commonjs/layouts/parallax.d.ts.map +1 -1
- package/lib/typescript/commonjs/public-types.d.ts +1 -0
- package/lib/typescript/commonjs/public-types.d.ts.map +1 -1
- package/lib/typescript/commonjs/store/index.d.ts +1 -1
- package/lib/typescript/commonjs/store/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/web-only-dependencies.d.ts +7 -0
- package/lib/typescript/commonjs/utils/web-only-dependencies.d.ts.map +1 -0
- package/lib/typescript/module/components/CarouselLayout.d.ts.map +1 -1
- package/lib/typescript/module/components/ItemLayout.d.ts.map +1 -1
- package/lib/typescript/module/components/ItemRenderer.d.ts.map +1 -1
- package/lib/typescript/module/components/Pagination/PaginationItem.d.ts.map +1 -1
- package/lib/typescript/module/components/ScrollViewGesture.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useCarouselController.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useCommonVariables.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useInitProps.d.ts +2 -1
- package/lib/typescript/module/hooks/useInitProps.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useOffsetX.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useOnProgressChange.d.ts.map +1 -1
- package/lib/typescript/module/hooks/useVisibleRanges.d.ts +1 -0
- package/lib/typescript/module/hooks/useVisibleRanges.d.ts.map +1 -1
- package/lib/typescript/module/layouts/normal.d.ts +2 -9
- package/lib/typescript/module/layouts/normal.d.ts.map +1 -1
- package/lib/typescript/module/layouts/parallax.d.ts +2 -16
- package/lib/typescript/module/layouts/parallax.d.ts.map +1 -1
- package/lib/typescript/module/public-types.d.ts +1 -0
- package/lib/typescript/module/public-types.d.ts.map +1 -1
- package/lib/typescript/module/store/index.d.ts +1 -1
- package/lib/typescript/module/store/index.d.ts.map +1 -1
- package/lib/typescript/module/utils/web-only-dependencies.d.ts +7 -0
- package/lib/typescript/module/utils/web-only-dependencies.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/components/CarouselLayout.tsx +15 -4
- package/src/components/ItemLayout.tsx +14 -4
- package/src/components/ItemRenderer.tsx +36 -4
- package/src/components/Pagination/PaginationItem.tsx +22 -20
- package/src/components/ScrollViewGesture.tsx +12 -5
- package/src/hooks/useCarouselController.tsx +3 -2
- package/src/hooks/useCommonVariables.ts +3 -2
- package/src/hooks/useInitProps.ts +13 -0
- package/src/hooks/useOffsetX.ts +11 -1
- package/src/hooks/useOnProgressChange.ts +2 -1
- package/src/hooks/useVisibleRanges.tsx +5 -2
- package/src/layouts/normal.ts +2 -1
- package/src/layouts/parallax.ts +2 -1
- package/src/public-types.ts +1 -0
- package/src/store/index.tsx +1 -1
- package/src/utils/web-only-dependencies.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-reanimated-carousel",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"packageManager": "yarn@4.0.2",
|
|
5
5
|
"description": "A performant, customizable carousel for React Native powered by Reanimated.",
|
|
6
6
|
"author": "Doho <zhaodonghao586@outlook.com> (https://github.com/dohooo)",
|
|
@@ -54,8 +54,9 @@
|
|
|
54
54
|
"test:watch": "jest --watch",
|
|
55
55
|
"test:coverage": "jest --coverage",
|
|
56
56
|
"test:ci": "jest --coverage --ci --runInBand --reporters=default --reporters=jest-junit",
|
|
57
|
-
"types": "tsc --noEmit && yarn types:public",
|
|
57
|
+
"types": "tsc --noEmit && yarn types:public && yarn types:non-strict",
|
|
58
58
|
"types:public": "tsc -p tsconfig.type-tests.json",
|
|
59
|
+
"types:non-strict": "tsc -p tsconfig.non-strict.json",
|
|
59
60
|
"lint": "biome check .",
|
|
60
61
|
"lint:fix": "biome check --write .",
|
|
61
62
|
"format": "biome format .",
|
|
@@ -8,6 +8,7 @@ import { useLayoutConfig } from "../hooks/useLayoutConfig";
|
|
|
8
8
|
import { useOnProgressChange } from "../hooks/useOnProgressChange";
|
|
9
9
|
import { useGlobalState } from "../store";
|
|
10
10
|
import type { CarouselRef } from "../types";
|
|
11
|
+
import { webOnlyDependencies } from "../utils/web-only-dependencies";
|
|
11
12
|
import { ItemRenderer } from "./ItemRenderer";
|
|
12
13
|
import { ScrollViewGesture } from "./ScrollViewGesture";
|
|
13
14
|
|
|
@@ -66,11 +67,13 @@ export const CarouselLayout = React.forwardRef<CarouselRef>((_props, ref) => {
|
|
|
66
67
|
const { size, handlerOffset, resolvedSize, sizePhase, sizeExplicit } = common;
|
|
67
68
|
const layoutConfig = useLayoutConfig({ ...props, size });
|
|
68
69
|
|
|
70
|
+
const isSizeReadyDependencies = webOnlyDependencies([resolvedSize, sizePhase]);
|
|
69
71
|
const isSizeReady = useDerivedValue(() => {
|
|
70
72
|
const currentSize = resolvedSize.value ?? 0;
|
|
71
73
|
return sizePhase.value === "ready" && currentSize > 0;
|
|
72
|
-
},
|
|
74
|
+
}, isSizeReadyDependencies);
|
|
73
75
|
|
|
76
|
+
const offsetXDependencies = webOnlyDependencies([loop, dataLength, handlerOffset, resolvedSize]);
|
|
74
77
|
const offsetX = useDerivedValue(() => {
|
|
75
78
|
const currentSize = resolvedSize.value ?? 0;
|
|
76
79
|
if (currentSize <= 0) return 0;
|
|
@@ -83,7 +86,7 @@ export const CarouselLayout = React.forwardRef<CarouselRef>((_props, ref) => {
|
|
|
83
86
|
|
|
84
87
|
const x = value % totalSize;
|
|
85
88
|
return Number.isNaN(x) ? 0 : x;
|
|
86
|
-
},
|
|
89
|
+
}, offsetXDependencies);
|
|
87
90
|
|
|
88
91
|
useOnProgressChange({
|
|
89
92
|
loop,
|
|
@@ -135,7 +138,7 @@ export const CarouselLayout = React.forwardRef<CarouselRef>((_props, ref) => {
|
|
|
135
138
|
if (state.ready) scheduleOnRN(triggerAutoPlay);
|
|
136
139
|
else scheduleOnRN(pauseAutoPlay);
|
|
137
140
|
},
|
|
138
|
-
[autoplay]
|
|
141
|
+
webOnlyDependencies([autoplay])
|
|
139
142
|
);
|
|
140
143
|
|
|
141
144
|
const scrollViewGestureOnScrollStart = React.useCallback(() => {
|
|
@@ -158,6 +161,14 @@ export const CarouselLayout = React.forwardRef<CarouselRef>((_props, ref) => {
|
|
|
158
161
|
StyleSheet.flatten(contentContainerStyle as StyleProp<ViewStyle>) || {};
|
|
159
162
|
const flattenedStyle = StyleSheet.flatten(style) || {};
|
|
160
163
|
|
|
164
|
+
const layoutStyleDependencies = webOnlyDependencies([
|
|
165
|
+
flattenedStyle,
|
|
166
|
+
isSizeReady,
|
|
167
|
+
isVertical,
|
|
168
|
+
resolvedSize,
|
|
169
|
+
sizePhase,
|
|
170
|
+
sizeExplicit,
|
|
171
|
+
]);
|
|
161
172
|
const layoutStyle = useAnimatedStyle(() => {
|
|
162
173
|
const measuredSize = resolvedSize.value ?? 0;
|
|
163
174
|
const { width, height } = resolveCarouselLayoutStyle({
|
|
@@ -172,7 +183,7 @@ export const CarouselLayout = React.forwardRef<CarouselRef>((_props, ref) => {
|
|
|
172
183
|
height,
|
|
173
184
|
opacity: isSizeReady.value ? 1 : 0,
|
|
174
185
|
};
|
|
175
|
-
},
|
|
186
|
+
}, layoutStyleDependencies);
|
|
176
187
|
|
|
177
188
|
return (
|
|
178
189
|
<View testID={testID} style={[styles.layoutContainer, style]} onLayout={props.onLayout}>
|
|
@@ -18,6 +18,7 @@ import type { CarouselItemAnimation } from "../types";
|
|
|
18
18
|
import { getHorizontalStackOffsetType } from "../utils/carousel-direction";
|
|
19
19
|
import { positiveModulo } from "../utils/carousel-math";
|
|
20
20
|
import { sanitizeAnimationStyle } from "../utils/sanitize-animation-style";
|
|
21
|
+
import { webOnlyDependencies } from "../utils/web-only-dependencies";
|
|
21
22
|
|
|
22
23
|
export type ItemAnimationStyle = CarouselItemAnimation;
|
|
23
24
|
|
|
@@ -67,6 +68,13 @@ export const ItemLayout: React.FC<{
|
|
|
67
68
|
const size = (itemSize ?? fallbackSize) || 0;
|
|
68
69
|
const effectivePageSize = size > 0 ? size : undefined;
|
|
69
70
|
|
|
71
|
+
const dimensionsStyleDependencies = webOnlyDependencies([
|
|
72
|
+
effectivePageSize,
|
|
73
|
+
isVertical,
|
|
74
|
+
itemSize,
|
|
75
|
+
styleHeightNumber,
|
|
76
|
+
styleWidthNumber,
|
|
77
|
+
]);
|
|
70
78
|
const dimensionsStyle = useAnimatedStyle<ViewStyle>(() => {
|
|
71
79
|
const widthCandidate = isVertical ? styleWidthNumber : itemSize;
|
|
72
80
|
const heightCandidate = isVertical ? itemSize : styleHeightNumber;
|
|
@@ -95,7 +103,7 @@ export const ItemLayout: React.FC<{
|
|
|
95
103
|
width: computedWidth,
|
|
96
104
|
height: computedHeight,
|
|
97
105
|
};
|
|
98
|
-
},
|
|
106
|
+
}, dimensionsStyleDependencies);
|
|
99
107
|
|
|
100
108
|
let offsetXConfig: OffsetOptions = {
|
|
101
109
|
handlerOffset,
|
|
@@ -137,17 +145,19 @@ export const ItemLayout: React.FC<{
|
|
|
137
145
|
scheduleOnRN(setIsAccessibilityCurrent, isCurrent);
|
|
138
146
|
}
|
|
139
147
|
},
|
|
140
|
-
[isCurrentForAccessibility]
|
|
148
|
+
webOnlyDependencies([isCurrentForAccessibility])
|
|
141
149
|
);
|
|
142
150
|
|
|
151
|
+
const relativeProgressDependencies = webOnlyDependencies([x, size]);
|
|
143
152
|
const relativeProgress = useDerivedValue(() => {
|
|
144
153
|
if (!size) return 0;
|
|
145
154
|
return x.value / size;
|
|
146
|
-
},
|
|
155
|
+
}, relativeProgressDependencies);
|
|
156
|
+
const animatedStyleDependencies = webOnlyDependencies([animationStyle, rawIndex, x, size]);
|
|
147
157
|
const animatedStyle = useAnimatedStyle<ViewStyle>(() => {
|
|
148
158
|
const safeSize = size || 1;
|
|
149
159
|
return sanitizeAnimationStyle(animationStyle(x.value / safeSize, rawIndex));
|
|
150
|
-
},
|
|
160
|
+
}, animatedStyleDependencies);
|
|
151
161
|
|
|
152
162
|
// TODO: For dynamic dimension in the future
|
|
153
163
|
// function handleLayout(e: LayoutChangeEvent) {
|
|
@@ -8,9 +8,14 @@ import type { ItemAnimationStyle } from "./ItemLayout";
|
|
|
8
8
|
import { ItemLayout } from "./ItemLayout";
|
|
9
9
|
|
|
10
10
|
import type { VisibleRanges } from "../hooks/useVisibleRanges";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
computeVisibleRanges,
|
|
13
|
+
normalizeWindowSize,
|
|
14
|
+
useVisibleRanges,
|
|
15
|
+
} from "../hooks/useVisibleRanges";
|
|
12
16
|
import type { CarouselItemAnimation, CarouselRenderItem } from "../types";
|
|
13
17
|
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
18
|
+
import { webOnlyDependencies } from "../utils/web-only-dependencies";
|
|
14
19
|
|
|
15
20
|
interface Props {
|
|
16
21
|
data: unknown[];
|
|
@@ -71,12 +76,34 @@ export const ItemRenderer: FC<Props> = (props) => {
|
|
|
71
76
|
|
|
72
77
|
const [displayedItems, setDisplayedItems] = React.useState<VisibleRanges>(initialRanges);
|
|
73
78
|
|
|
79
|
+
// When the window covers every item, the ranges always span the whole list. Skip them so a
|
|
80
|
+
// stale range from before a data change cannot unmount keyed items for one render.
|
|
81
|
+
const rendersAllItems = normalizeWindowSize(dataLength, renderWindowSize) >= dataLength;
|
|
82
|
+
|
|
74
83
|
useAnimatedReaction(
|
|
75
84
|
() => visibleRanges.value,
|
|
76
85
|
(ranges) => scheduleOnRN(setDisplayedItems, ranges),
|
|
77
|
-
[visibleRanges]
|
|
86
|
+
webOnlyDependencies([visibleRanges])
|
|
78
87
|
);
|
|
79
88
|
|
|
89
|
+
// With a render window, the ranges still describe the previous data for the renders between a
|
|
90
|
+
// data change and the next range update. Keep the keyed items mounted with the last up-to-date
|
|
91
|
+
// ranges until then, so an item that shifted index is not unmounted and mounted again.
|
|
92
|
+
const lastSettledRender = React.useRef<{
|
|
93
|
+
data: unknown[];
|
|
94
|
+
ranges: VisibleRanges;
|
|
95
|
+
keys: ReadonlySet<string>;
|
|
96
|
+
} | null>(null);
|
|
97
|
+
const settled = lastSettledRender.current;
|
|
98
|
+
const rangesAreStale =
|
|
99
|
+
!!keyExtractor && !!settled && settled.ranges === displayedItems && settled.data !== data;
|
|
100
|
+
const renderedKeys = new Set<string>();
|
|
101
|
+
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
if (rangesAreStale) return;
|
|
104
|
+
lastSettledRender.current = { data, ranges: displayedItems, keys: renderedKeys };
|
|
105
|
+
});
|
|
106
|
+
|
|
80
107
|
return (
|
|
81
108
|
<>
|
|
82
109
|
{data.map((item, index) => {
|
|
@@ -89,11 +116,12 @@ export const ItemRenderer: FC<Props> = (props) => {
|
|
|
89
116
|
|
|
90
117
|
const { negativeRange, positiveRange } = displayedItems;
|
|
91
118
|
|
|
92
|
-
const
|
|
119
|
+
const isInRange =
|
|
120
|
+
rendersAllItems ||
|
|
93
121
|
(index >= negativeRange[0] && index <= negativeRange[1]) ||
|
|
94
122
|
(index >= positiveRange[0] && index <= positiveRange[1]);
|
|
95
123
|
|
|
96
|
-
if (!
|
|
124
|
+
if (!isInRange && !rangesAreStale) return null;
|
|
97
125
|
|
|
98
126
|
const rawItem = rawData[realIndex] ?? item;
|
|
99
127
|
const key = keyExtractor
|
|
@@ -102,6 +130,10 @@ export const ItemRenderer: FC<Props> = (props) => {
|
|
|
102
130
|
)}`
|
|
103
131
|
: String(index);
|
|
104
132
|
|
|
133
|
+
if (!isInRange && !settled?.keys.has(key)) return null;
|
|
134
|
+
|
|
135
|
+
renderedKeys.add(key);
|
|
136
|
+
|
|
105
137
|
return (
|
|
106
138
|
<ItemLayout
|
|
107
139
|
key={key}
|
|
@@ -12,6 +12,7 @@ import { scheduleOnRN } from "react-native-worklets";
|
|
|
12
12
|
|
|
13
13
|
import type { PaginationDotStyle, PaginationProps } from "../../public-types";
|
|
14
14
|
import { getNearestLoopPosition, getSettledRawIndex } from "../../utils/carousel-math";
|
|
15
|
+
import { webOnlyDependencies } from "../../utils/web-only-dependencies";
|
|
15
16
|
|
|
16
17
|
const DEFAULT_DOT_SIZE = 10;
|
|
17
18
|
const DEFAULT_DOT_COLOR = "#D1D5DB";
|
|
@@ -86,9 +87,28 @@ export function PaginationItem(props: {
|
|
|
86
87
|
scheduleOnRN(setSelected, nextSelected);
|
|
87
88
|
}
|
|
88
89
|
},
|
|
89
|
-
[count, index, progress]
|
|
90
|
+
webOnlyDependencies([count, index, progress])
|
|
90
91
|
);
|
|
91
92
|
|
|
93
|
+
const animatedStyleDependencies = webOnlyDependencies([
|
|
94
|
+
activeBackgroundColor,
|
|
95
|
+
activeBorderColor,
|
|
96
|
+
activeBorderRadius,
|
|
97
|
+
activeBorderWidth,
|
|
98
|
+
activeHeight,
|
|
99
|
+
activeOpacity,
|
|
100
|
+
activeWidth,
|
|
101
|
+
baseBackgroundColor,
|
|
102
|
+
baseBorderColor,
|
|
103
|
+
baseBorderRadius,
|
|
104
|
+
baseBorderWidth,
|
|
105
|
+
baseHeight,
|
|
106
|
+
baseOpacity,
|
|
107
|
+
baseWidth,
|
|
108
|
+
count,
|
|
109
|
+
index,
|
|
110
|
+
progress,
|
|
111
|
+
]);
|
|
92
112
|
const animatedStyle = useAnimatedStyle<ViewStyle>(() => {
|
|
93
113
|
const distance = Math.min(1, getPaginationDotDistance(progress.value, index, count));
|
|
94
114
|
|
|
@@ -111,25 +131,7 @@ export function PaginationItem(props: {
|
|
|
111
131
|
backgroundColor: interpolateDotColor(distance, activeBackgroundColor, baseBackgroundColor),
|
|
112
132
|
opacity: interpolate(distance, [0, 1], [activeOpacity, baseOpacity], Extrapolation.CLAMP),
|
|
113
133
|
};
|
|
114
|
-
},
|
|
115
|
-
activeBackgroundColor,
|
|
116
|
-
activeBorderColor,
|
|
117
|
-
activeBorderRadius,
|
|
118
|
-
activeBorderWidth,
|
|
119
|
-
activeHeight,
|
|
120
|
-
activeOpacity,
|
|
121
|
-
activeWidth,
|
|
122
|
-
baseBackgroundColor,
|
|
123
|
-
baseBorderColor,
|
|
124
|
-
baseBorderRadius,
|
|
125
|
-
baseBorderWidth,
|
|
126
|
-
baseHeight,
|
|
127
|
-
baseOpacity,
|
|
128
|
-
baseWidth,
|
|
129
|
-
count,
|
|
130
|
-
index,
|
|
131
|
-
progress,
|
|
132
|
-
]);
|
|
134
|
+
}, animatedStyleDependencies);
|
|
133
135
|
|
|
134
136
|
const reservedStyle: ViewStyle = {
|
|
135
137
|
alignItems: "center",
|
|
@@ -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
|
-
},
|
|
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
|
-
|
|
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(
|
|
@@ -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)
|
package/src/hooks/useOffsetX.ts
CHANGED
|
@@ -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
|
-
},
|
|
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
|
-
},
|
|
134
|
+
}, rangesDependencies);
|
|
132
135
|
|
|
133
136
|
return ranges;
|
|
134
137
|
}
|
package/src/layouts/normal.ts
CHANGED
|
@@ -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
|
package/src/layouts/parallax.ts
CHANGED
|
@@ -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,
|
package/src/public-types.ts
CHANGED
package/src/store/index.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { SharedValue, useSharedValue } from "react-native-reanimated";
|
|
3
|
+
import { type SharedValue, useSharedValue } from "react-native-reanimated";
|
|
4
4
|
import type { CarouselCommonVariables } from "../hooks/useCommonVariables";
|
|
5
5
|
import type { InitializedCarouselProps } from "../hooks/useInitProps";
|
|
6
6
|
|
|
@@ -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
|
+
}
|