@r0b0t3d/react-native-carousel 3.4.7 → 3.4.9
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/package.json +1 -1
- package/src/components/Carousel.tsx +33 -24
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import React, {
|
|
|
5
5
|
useMemo,
|
|
6
6
|
useRef,
|
|
7
7
|
} from 'react';
|
|
8
|
-
import {
|
|
8
|
+
import { StyleSheet, Platform, AppState, useWindowDimensions } from 'react-native';
|
|
9
9
|
import { useInterval } from '@r0b0t3d/react-native-hooks';
|
|
10
10
|
import Animated, {
|
|
11
11
|
useSharedValue,
|
|
@@ -31,11 +31,13 @@ import { useInternalCarouselContext } from './useInternalCarouselContext';
|
|
|
31
31
|
// Transitions:
|
|
32
32
|
//
|
|
33
33
|
// IDLE ── onBeginDrag ──► DRAGGING
|
|
34
|
+
// IDLE ── programmatic scroll ──► (stays IDLE, onMomentumEnd fires)
|
|
34
35
|
// DRAGGING ── onMomentumEnd ──► IDLE (or LOOP_JUMP at boundary)
|
|
35
36
|
// LOOP_JUMP ── onScroll (arrival) ──► IDLE
|
|
36
37
|
// LOOP_JUMP ── onBeginDrag (user overrides) ──► DRAGGING
|
|
37
38
|
//
|
|
38
|
-
// Page change (onPageChange / animatedPage)
|
|
39
|
+
// Page change (onPageChange / animatedPage) fires from onMomentumEnd for
|
|
40
|
+
// both user drags and programmatic scrolls (goNext / goPrev / snapToItem).
|
|
39
41
|
// Autoplay only advances when state === IDLE.
|
|
40
42
|
// ---------------------------------------------------------------------------
|
|
41
43
|
|
|
@@ -54,10 +56,6 @@ enum CarouselState {
|
|
|
54
56
|
// All consumer-facing callbacks receive logical indices.
|
|
55
57
|
// ---------------------------------------------------------------------------
|
|
56
58
|
|
|
57
|
-
const getScreenWidth = () => {
|
|
58
|
-
return Dimensions.get('screen').width;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
59
|
function Carousel<TData>({
|
|
62
60
|
style,
|
|
63
61
|
data,
|
|
@@ -67,8 +65,8 @@ function Carousel<TData>({
|
|
|
67
65
|
autoPlay = false,
|
|
68
66
|
duration = 1000,
|
|
69
67
|
animation,
|
|
70
|
-
sliderWidth
|
|
71
|
-
itemWidth
|
|
68
|
+
sliderWidth,
|
|
69
|
+
itemWidth,
|
|
72
70
|
firstItemAlignment = 'center',
|
|
73
71
|
inactiveOpacity = 1,
|
|
74
72
|
inactiveScale = 1,
|
|
@@ -82,6 +80,10 @@ function Carousel<TData>({
|
|
|
82
80
|
disableItemPress = false,
|
|
83
81
|
scrollViewRef: externalScrollViewRef,
|
|
84
82
|
}: CarouselProps<TData>) {
|
|
83
|
+
const { width: screenWidth } = useWindowDimensions();
|
|
84
|
+
const effectiveSliderWidth = sliderWidth ?? screenWidth;
|
|
85
|
+
const effectiveItemWidth = itemWidth ?? screenWidth;
|
|
86
|
+
|
|
85
87
|
// ---- shared values (UI-thread state) ------------------------------------
|
|
86
88
|
|
|
87
89
|
const currentRenderPage = useSharedValue(0);
|
|
@@ -105,18 +107,18 @@ function Carousel<TData>({
|
|
|
105
107
|
// ---- derived values -----------------------------------------------------
|
|
106
108
|
|
|
107
109
|
const horizontalPadding = useMemo(() => {
|
|
108
|
-
const padding = (
|
|
110
|
+
const padding = (effectiveSliderWidth - effectiveItemWidth) / 2;
|
|
109
111
|
return firstItemAlignment === 'center' || loop ? padding : spaceHeadTail;
|
|
110
|
-
}, [
|
|
112
|
+
}, [effectiveSliderWidth, effectiveItemWidth, firstItemAlignment, loop, spaceHeadTail]);
|
|
111
113
|
|
|
112
114
|
const offsets = useMemo(() => {
|
|
113
115
|
return generateOffsets({
|
|
114
|
-
sliderWidth,
|
|
115
|
-
itemWidth,
|
|
116
|
+
sliderWidth: effectiveSliderWidth,
|
|
117
|
+
itemWidth: effectiveItemWidth,
|
|
116
118
|
itemCount: data.length + (loop ? additionalPagesPerSide * 2 : 0),
|
|
117
119
|
horizontalPadding,
|
|
118
120
|
});
|
|
119
|
-
}, [
|
|
121
|
+
}, [effectiveSliderWidth, effectiveItemWidth, data.length, loop, additionalPagesPerSide, horizontalPadding]);
|
|
120
122
|
|
|
121
123
|
const pageItems = useMemo(() => {
|
|
122
124
|
if (!data || data.length === 0) {
|
|
@@ -133,7 +135,7 @@ function Carousel<TData>({
|
|
|
133
135
|
return data;
|
|
134
136
|
}, [data, loop, additionalPagesPerSide]);
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
// ---- page-change callback (JS thread) -----------------------------------
|
|
137
139
|
|
|
138
140
|
const handlePageChange = useCallback(
|
|
139
141
|
(logicalPage: number) => {
|
|
@@ -185,7 +187,7 @@ function Carousel<TData>({
|
|
|
185
187
|
if (safeLogicalIdx !== prevLogical) {
|
|
186
188
|
handlePageChange(safeLogicalIdx);
|
|
187
189
|
}
|
|
188
|
-
}, [data.length,
|
|
190
|
+
}, [data.length, effectiveSliderWidth, effectiveItemWidth, loop, additionalPagesPerSide, offsets.length, handlePageChange]);
|
|
189
191
|
|
|
190
192
|
// ---- app-state listener (pause autoplay when backgrounded) --------------
|
|
191
193
|
|
|
@@ -316,6 +318,12 @@ function Carousel<TData>({
|
|
|
316
318
|
|
|
317
319
|
// ---- scroll handler (UI thread worklet) ---------------------------------
|
|
318
320
|
|
|
321
|
+
// Snapshot as a primitive so the worklets below close over a number,
|
|
322
|
+
// not the `data` array itself — `data` items may contain non-shareable
|
|
323
|
+
// values (e.g. JSX elements), which Reanimated can't clone for the UI
|
|
324
|
+
// thread ([Worklets] Cannot copy value of type `FiberNode`).
|
|
325
|
+
const dataLength = data.length;
|
|
326
|
+
|
|
319
327
|
const scrollHandler = useAnimatedScrollHandler(
|
|
320
328
|
{
|
|
321
329
|
onScroll: (event) => {
|
|
@@ -352,9 +360,10 @@ function Carousel<TData>({
|
|
|
352
360
|
},
|
|
353
361
|
|
|
354
362
|
onMomentumEnd: (event) => {
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
|
|
363
|
+
// Process page changes after both user drags and programmatic
|
|
364
|
+
// scrolls (goNext / goPrev / snapToItem). Loop jumps are
|
|
365
|
+
// instant scrolls that are resolved via onScroll instead.
|
|
366
|
+
if (carouselState.value === CarouselState.LOOP_JUMP) {
|
|
358
367
|
return;
|
|
359
368
|
}
|
|
360
369
|
|
|
@@ -371,7 +380,7 @@ function Carousel<TData>({
|
|
|
371
380
|
if (loop) {
|
|
372
381
|
const boundaryTarget = getLoopBoundaryTarget(
|
|
373
382
|
renderPage,
|
|
374
|
-
|
|
383
|
+
dataLength,
|
|
375
384
|
additionalPagesPerSide
|
|
376
385
|
);
|
|
377
386
|
if (boundaryTarget !== -1) {
|
|
@@ -387,7 +396,7 @@ function Carousel<TData>({
|
|
|
387
396
|
currentRenderPage.value = boundaryTarget;
|
|
388
397
|
currentLogicalPage.value = getLogicalPage(
|
|
389
398
|
boundaryTarget,
|
|
390
|
-
|
|
399
|
+
dataLength,
|
|
391
400
|
additionalPagesPerSide,
|
|
392
401
|
loop
|
|
393
402
|
);
|
|
@@ -401,7 +410,7 @@ function Carousel<TData>({
|
|
|
401
410
|
currentRenderPage.value = renderPage;
|
|
402
411
|
currentLogicalPage.value = getLogicalPage(
|
|
403
412
|
renderPage,
|
|
404
|
-
|
|
413
|
+
dataLength,
|
|
405
414
|
additionalPagesPerSide,
|
|
406
415
|
loop
|
|
407
416
|
);
|
|
@@ -413,7 +422,7 @@ function Carousel<TData>({
|
|
|
413
422
|
[
|
|
414
423
|
offsets,
|
|
415
424
|
loop,
|
|
416
|
-
|
|
425
|
+
dataLength,
|
|
417
426
|
additionalPagesPerSide,
|
|
418
427
|
handlePageChange,
|
|
419
428
|
internalAnimatedRef,
|
|
@@ -489,7 +498,7 @@ function Carousel<TData>({
|
|
|
489
498
|
item={item}
|
|
490
499
|
index={logicalIndex}
|
|
491
500
|
offset={offsets[i]}
|
|
492
|
-
itemWidth={
|
|
501
|
+
itemWidth={effectiveItemWidth}
|
|
493
502
|
animatedValue={animatedScroll}
|
|
494
503
|
animation={animation}
|
|
495
504
|
renderItem={renderItem}
|
|
@@ -507,7 +516,7 @@ function Carousel<TData>({
|
|
|
507
516
|
getItemKey,
|
|
508
517
|
containerStyles,
|
|
509
518
|
offsets,
|
|
510
|
-
|
|
519
|
+
effectiveItemWidth,
|
|
511
520
|
animatedScroll,
|
|
512
521
|
animation,
|
|
513
522
|
renderItem,
|