react-native-reanimated-carousel 4.0.0-alpha.6 → 4.0.0-alpha.8
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/lib/commonjs/components/BaseLayout.js +2 -20
- package/lib/commonjs/components/BaseLayout.js.map +1 -1
- package/lib/commonjs/components/Carousel.js +19 -41
- package/lib/commonjs/components/Carousel.js.map +1 -1
- package/lib/commonjs/components/ItemRenderer.js +80 -0
- package/lib/commonjs/components/ItemRenderer.js.map +1 -0
- package/lib/commonjs/hooks/useCarouselController.js +4 -3
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
- package/lib/commonjs/hooks/useOffsetX.test.js.map +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js +36 -18
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
- package/lib/commonjs/utils/handleroffset-direction.js +5 -5
- package/lib/commonjs/utils/handleroffset-direction.js.map +1 -1
- package/lib/commonjs/utils/handleroffset-direction.test.js +46 -0
- package/lib/commonjs/utils/handleroffset-direction.test.js.map +1 -0
- package/lib/module/components/BaseLayout.js +3 -15
- package/lib/module/components/BaseLayout.js.map +1 -1
- package/lib/module/components/Carousel.js +19 -38
- package/lib/module/components/Carousel.js.map +1 -1
- package/lib/module/components/ItemRenderer.js +62 -0
- package/lib/module/components/ItemRenderer.js.map +1 -0
- package/lib/module/hooks/useCarouselController.js +4 -3
- package/lib/module/hooks/useCarouselController.js.map +1 -1
- package/lib/module/hooks/useOffsetX.test.js.map +1 -1
- package/lib/module/hooks/useVisibleRanges.js +35 -18
- package/lib/module/hooks/useVisibleRanges.js.map +1 -1
- package/lib/module/utils/handleroffset-direction.js +5 -5
- package/lib/module/utils/handleroffset-direction.js.map +1 -1
- package/lib/module/utils/handleroffset-direction.test.js +41 -0
- package/lib/module/utils/handleroffset-direction.test.js.map +1 -0
- package/lib/typescript/components/ItemRenderer.d.ts +22 -0
- package/lib/typescript/hooks/useCarouselController.d.ts +2 -1
- package/lib/typescript/hooks/useVisibleRanges.d.ts +7 -4
- package/lib/typescript/utils/handleroffset-direction.d.ts +2 -1
- package/lib/typescript/utils/handleroffset-direction.test.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/BaseLayout.tsx +3 -33
- package/src/components/Carousel.tsx +18 -53
- package/src/components/ItemRenderer.tsx +105 -0
- package/src/hooks/useCarouselController.tsx +16 -13
- package/src/hooks/useOffsetX.test.ts +1 -1
- package/src/hooks/useVisibleRanges.tsx +56 -24
- package/src/utils/handleroffset-direction.test.ts +52 -0
- package/src/utils/handleroffset-direction.ts +12 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useVisibleRanges.tsx"],"names":["useDerivedValue","useVisibleRanges","options","total","viewSize","translation","windowSize","_windowSize","loop","ranges","positiveCount","Math","round","negativeCount","currentIndex","value","negativeRange","positiveRange"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["useVisibleRanges.tsx"],"names":["useRef","useDerivedValue","useVisibleRanges","options","total","viewSize","translation","windowSize","_windowSize","loop","cachedRanges","ranges","positiveCount","Math","round","negativeCount","currentIndex","value","newRanges","negativeRange","positiveRange","isArraysEqual","current","a","b","length","every","index"],"mappings":"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","sourcesContent":["import { useRef } from \"react\";\nimport type Animated from \"react-native-reanimated\";\nimport { useDerivedValue } from \"react-native-reanimated\";\n\ntype Range = [number, number];\n\nexport interface VisibleRanges {\n negativeRange: Range\n positiveRange: Range\n}\n\nexport type IVisibleRanges = Animated.SharedValue<VisibleRanges>;\n\nexport function useVisibleRanges(options: {\n total: number\n viewSize: number\n windowSize?: number\n translation: Animated.SharedValue<number>\n loop?: boolean\n}): IVisibleRanges {\n const {\n total = 0,\n viewSize,\n translation,\n windowSize: _windowSize,\n loop,\n } = options;\n\n const windowSize = _windowSize ?? total;\n const cachedRanges = useRef<VisibleRanges>(null!);\n\n const ranges = useDerivedValue(() => {\n const positiveCount = Math.round(windowSize / 2);\n const negativeCount = windowSize - positiveCount;\n\n let currentIndex = Math.round(-translation.value / viewSize);\n currentIndex = currentIndex < 0 ? (currentIndex % total) + total : currentIndex;\n\n let newRanges: VisibleRanges;\n\n if (!loop) {\n // Adjusting negative range if the carousel is not loopable.\n // So, It will be only displayed the positive items.\n newRanges = {\n negativeRange: [0 + currentIndex - (windowSize - 1), 0 + currentIndex],\n positiveRange: [0 + currentIndex, currentIndex + (windowSize - 1)],\n };\n }\n else {\n const negativeRange: Range = [\n (currentIndex - negativeCount + total) % total,\n (currentIndex - 1 + total) % total,\n ];\n\n const positiveRange: Range = [\n (currentIndex + total) % total,\n (currentIndex + positiveCount + total) % total,\n ];\n\n if (negativeRange[0] < total && negativeRange[0] > negativeRange[1]) {\n negativeRange[1] = total - 1;\n positiveRange[0] = 0;\n }\n if (positiveRange[0] > positiveRange[1]) {\n negativeRange[1] = total - 1;\n positiveRange[0] = 0;\n }\n\n // console.log({ negativeRange, positiveRange ,total,windowSize,a:total <= _windowSize})\n newRanges = { negativeRange, positiveRange };\n }\n\n if (\n isArraysEqual(\n cachedRanges.current?.negativeRange ?? [],\n newRanges.negativeRange,\n )\n && isArraysEqual(\n cachedRanges.current?.positiveRange ?? [],\n newRanges.positiveRange,\n )\n )\n return cachedRanges.current;\n\n cachedRanges.current = newRanges;\n return cachedRanges.current;\n }, [loop, total, windowSize, translation]);\n\n return ranges;\n}\n\nfunction isArraysEqual(a: number[], b: number[]): boolean {\n \"worklet\";\n if (a.length !== b.length) return false;\n\n return a.every((value, index) => value === b[index]);\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export function handlerOffsetDirection(handlerOffset) {
|
|
1
|
+
export function handlerOffsetDirection(handlerOffset, fixedDirection) {
|
|
2
2
|
"worklet";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return
|
|
4
|
+
if (fixedDirection === "negative") return -1;
|
|
5
|
+
if (fixedDirection === "positive") return 1;
|
|
6
|
+
if (handlerOffset.value === 0) return -1;
|
|
7
|
+
return Math.sign(handlerOffset.value);
|
|
8
8
|
}
|
|
9
9
|
//# sourceMappingURL=handleroffset-direction.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["handleroffset-direction.ts"],"names":["handlerOffsetDirection","handlerOffset","
|
|
1
|
+
{"version":3,"sources":["handleroffset-direction.ts"],"names":["handlerOffsetDirection","handlerOffset","fixedDirection","value","Math","sign"],"mappings":"AAIA,OAAO,SAASA,sBAAT,CAAgCC,aAAhC,EAAoEC,cAApE,EAA+H;AACpI;;AAEA,MAAIA,cAAc,KAAK,UAAvB,EACE,OAAO,CAAC,CAAR;AAEF,MAAIA,cAAc,KAAK,UAAvB,EACE,OAAO,CAAP;AAEF,MAAID,aAAa,CAACE,KAAd,KAAwB,CAA5B,EACE,OAAO,CAAC,CAAR;AAEF,SAAOC,IAAI,CAACC,IAAL,CAAUJ,aAAa,CAACE,KAAxB,CAAP;AACD","sourcesContent":["import type { SharedValue } from \"react-native-reanimated\";\n\nimport type { TCarouselProps } from \"../types\";\n\nexport function handlerOffsetDirection(handlerOffset: SharedValue<number>, fixedDirection?: TCarouselProps[\"fixedDirection\"]): -1 | 1 {\n \"worklet\";\n\n if (fixedDirection === \"negative\")\n return -1;\n\n if (fixedDirection === \"positive\")\n return 1;\n\n if (handlerOffset.value === 0)\n return -1;\n\n return Math.sign(handlerOffset.value) as -1 | 1;\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
2
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
3
|
+
import { handlerOffsetDirection } from "./handleroffset-direction";
|
|
4
|
+
describe("handlerOffsetDirection", () => {
|
|
5
|
+
it("should return -1 when default value equals to zero", () => {
|
|
6
|
+
const result = renderHook(() => {
|
|
7
|
+
const handlerOffsetAnimVal = useSharedValue(0);
|
|
8
|
+
return handlerOffsetDirection(handlerOffsetAnimVal);
|
|
9
|
+
});
|
|
10
|
+
expect(result.result.current).toBe(-1);
|
|
11
|
+
});
|
|
12
|
+
it("should return 1 when default value is greater than zero", () => {
|
|
13
|
+
const result = renderHook(() => {
|
|
14
|
+
const handlerOffsetAnimVal = useSharedValue(1);
|
|
15
|
+
return handlerOffsetDirection(handlerOffsetAnimVal);
|
|
16
|
+
});
|
|
17
|
+
expect(result.result.current).toBe(1);
|
|
18
|
+
});
|
|
19
|
+
it("should return -1 when default value is less than zero", () => {
|
|
20
|
+
const result = renderHook(() => {
|
|
21
|
+
const handlerOffsetAnimVal = useSharedValue(-1);
|
|
22
|
+
return handlerOffsetDirection(handlerOffsetAnimVal);
|
|
23
|
+
});
|
|
24
|
+
expect(result.result.current).toBe(-1);
|
|
25
|
+
});
|
|
26
|
+
it("should return 1 when default value equals to zero and fixedDirection is negative", () => {
|
|
27
|
+
const result = renderHook(() => {
|
|
28
|
+
const handlerOffsetAnimVal = useSharedValue(-1);
|
|
29
|
+
return handlerOffsetDirection(handlerOffsetAnimVal, "positive");
|
|
30
|
+
});
|
|
31
|
+
expect(result.result.current).toBe(1);
|
|
32
|
+
});
|
|
33
|
+
it("should return -1 when default value is greater than zero and fixedDirection is negative", () => {
|
|
34
|
+
const result = renderHook(() => {
|
|
35
|
+
const handlerOffsetAnimVal = useSharedValue(1);
|
|
36
|
+
return handlerOffsetDirection(handlerOffsetAnimVal, "negative");
|
|
37
|
+
});
|
|
38
|
+
expect(result.result.current).toBe(-1);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=handleroffset-direction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["handleroffset-direction.test.ts"],"names":["useSharedValue","renderHook","handlerOffsetDirection","describe","it","result","handlerOffsetAnimVal","expect","current","toBe"],"mappings":"AAAA,SAASA,cAAT,QAA+B,yBAA/B;AAEA,SAASC,UAAT,QAA2B,8BAA3B;AAEA,SAASC,sBAAT,QAAuC,2BAAvC;AAEAC,QAAQ,CAAC,wBAAD,EAA2B,MAAM;AACvCC,EAAAA,EAAE,CAAC,oDAAD,EAAuD,MAAM;AAC7D,UAAMC,MAAM,GAAGJ,UAAU,CAAC,MAAM;AAC9B,YAAMK,oBAAoB,GAAGN,cAAc,CAAC,CAAD,CAA3C;AACA,aAAOE,sBAAsB,CAACI,oBAAD,CAA7B;AACD,KAHwB,CAAzB;AAKAC,IAAAA,MAAM,CAACF,MAAM,CAACA,MAAP,CAAcG,OAAf,CAAN,CAA8BC,IAA9B,CAAmC,CAAC,CAApC;AACD,GAPC,CAAF;AASAL,EAAAA,EAAE,CAAC,yDAAD,EAA4D,MAAM;AAClE,UAAMC,MAAM,GAAGJ,UAAU,CAAC,MAAM;AAC9B,YAAMK,oBAAoB,GAAGN,cAAc,CAAC,CAAD,CAA3C;AACA,aAAOE,sBAAsB,CAACI,oBAAD,CAA7B;AACD,KAHwB,CAAzB;AAKAC,IAAAA,MAAM,CAACF,MAAM,CAACA,MAAP,CAAcG,OAAf,CAAN,CAA8BC,IAA9B,CAAmC,CAAnC;AACD,GAPC,CAAF;AASAL,EAAAA,EAAE,CAAC,uDAAD,EAA0D,MAAM;AAChE,UAAMC,MAAM,GAAGJ,UAAU,CAAC,MAAM;AAC9B,YAAMK,oBAAoB,GAAGN,cAAc,CAAC,CAAC,CAAF,CAA3C;AACA,aAAOE,sBAAsB,CAACI,oBAAD,CAA7B;AACD,KAHwB,CAAzB;AAKAC,IAAAA,MAAM,CAACF,MAAM,CAACA,MAAP,CAAcG,OAAf,CAAN,CAA8BC,IAA9B,CAAmC,CAAC,CAApC;AACD,GAPC,CAAF;AASAL,EAAAA,EAAE,CAAC,kFAAD,EAAqF,MAAM;AAC3F,UAAMC,MAAM,GAAGJ,UAAU,CAAC,MAAM;AAC9B,YAAMK,oBAAoB,GAAGN,cAAc,CAAC,CAAC,CAAF,CAA3C;AACA,aAAOE,sBAAsB,CAACI,oBAAD,EAAuB,UAAvB,CAA7B;AACD,KAHwB,CAAzB;AAKAC,IAAAA,MAAM,CAACF,MAAM,CAACA,MAAP,CAAcG,OAAf,CAAN,CAA8BC,IAA9B,CAAmC,CAAnC;AACD,GAPC,CAAF;AASAL,EAAAA,EAAE,CAAC,yFAAD,EAA4F,MAAM;AAClG,UAAMC,MAAM,GAAGJ,UAAU,CAAC,MAAM;AAC9B,YAAMK,oBAAoB,GAAGN,cAAc,CAAC,CAAD,CAA3C;AACA,aAAOE,sBAAsB,CAACI,oBAAD,EAAuB,UAAvB,CAA7B;AACD,KAHwB,CAAzB;AAKAC,IAAAA,MAAM,CAACF,MAAM,CAACA,MAAP,CAAcG,OAAf,CAAN,CAA8BC,IAA9B,CAAmC,CAAC,CAApC;AACD,GAPC,CAAF;AAQD,CA7CO,CAAR","sourcesContent":["import { useSharedValue } from \"react-native-reanimated\";\n\nimport { renderHook } from \"@testing-library/react-hooks\";\n\nimport { handlerOffsetDirection } from \"./handleroffset-direction\";\n\ndescribe(\"handlerOffsetDirection\", () => {\n it(\"should return -1 when default value equals to zero\", () => {\n const result = renderHook(() => {\n const handlerOffsetAnimVal = useSharedValue(0);\n return handlerOffsetDirection(handlerOffsetAnimVal);\n });\n\n expect(result.result.current).toBe(-1);\n });\n\n it(\"should return 1 when default value is greater than zero\", () => {\n const result = renderHook(() => {\n const handlerOffsetAnimVal = useSharedValue(1);\n return handlerOffsetDirection(handlerOffsetAnimVal);\n });\n\n expect(result.result.current).toBe(1);\n });\n\n it(\"should return -1 when default value is less than zero\", () => {\n const result = renderHook(() => {\n const handlerOffsetAnimVal = useSharedValue(-1);\n return handlerOffsetDirection(handlerOffsetAnimVal);\n });\n\n expect(result.result.current).toBe(-1);\n });\n\n it(\"should return 1 when default value equals to zero and fixedDirection is negative\", () => {\n const result = renderHook(() => {\n const handlerOffsetAnimVal = useSharedValue(-1);\n return handlerOffsetDirection(handlerOffsetAnimVal, \"positive\");\n });\n\n expect(result.result.current).toBe(1);\n });\n\n it(\"should return -1 when default value is greater than zero and fixedDirection is negative\", () => {\n const result = renderHook(() => {\n const handlerOffsetAnimVal = useSharedValue(1);\n return handlerOffsetDirection(handlerOffsetAnimVal, \"negative\");\n });\n\n expect(result.result.current).toBe(-1);\n });\n});\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
import type { ViewStyle } from "react-native";
|
|
3
|
+
import type Animated from "react-native-reanimated";
|
|
4
|
+
import { type AnimatedStyleProp } from "react-native-reanimated";
|
|
5
|
+
import type { TAnimationStyle } from "./BaseLayout";
|
|
6
|
+
import type { CarouselRenderItem } from "../types";
|
|
7
|
+
interface Props {
|
|
8
|
+
data: any[];
|
|
9
|
+
dataLength: number;
|
|
10
|
+
rawDataLength: number;
|
|
11
|
+
loop: boolean;
|
|
12
|
+
size: number;
|
|
13
|
+
windowSize?: number;
|
|
14
|
+
autoFillData: boolean;
|
|
15
|
+
offsetX: Animated.SharedValue<number>;
|
|
16
|
+
handlerOffset: Animated.SharedValue<number>;
|
|
17
|
+
layoutConfig: TAnimationStyle;
|
|
18
|
+
renderItem: CarouselRenderItem<any>;
|
|
19
|
+
customAnimation?: ((value: number) => AnimatedStyleProp<ViewStyle>);
|
|
20
|
+
}
|
|
21
|
+
export declare const ItemRenderer: FC<Props>;
|
|
22
|
+
export {};
|
|
@@ -4,9 +4,10 @@ interface IOpts {
|
|
|
4
4
|
loop: boolean;
|
|
5
5
|
size: number;
|
|
6
6
|
dataLength: number;
|
|
7
|
-
autoFillData: TCarouselProps["autoFillData"];
|
|
8
7
|
handlerOffset: Animated.SharedValue<number>;
|
|
8
|
+
autoFillData: TCarouselProps["autoFillData"];
|
|
9
9
|
withAnimation?: TCarouselProps["withAnimation"];
|
|
10
|
+
fixedDirection?: TCarouselProps["fixedDirection"];
|
|
10
11
|
duration?: number;
|
|
11
12
|
defaultIndex?: number;
|
|
12
13
|
onScrollBegin?: () => void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type Animated from "react-native-reanimated";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
declare type Range = [number, number];
|
|
3
|
+
export interface VisibleRanges {
|
|
4
|
+
negativeRange: Range;
|
|
5
|
+
positiveRange: Range;
|
|
6
|
+
}
|
|
7
|
+
export declare type IVisibleRanges = Animated.SharedValue<VisibleRanges>;
|
|
6
8
|
export declare function useVisibleRanges(options: {
|
|
7
9
|
total: number;
|
|
8
10
|
viewSize: number;
|
|
@@ -10,3 +12,4 @@ export declare function useVisibleRanges(options: {
|
|
|
10
12
|
translation: Animated.SharedValue<number>;
|
|
11
13
|
loop?: boolean;
|
|
12
14
|
}): IVisibleRanges;
|
|
15
|
+
export {};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { SharedValue } from "react-native-reanimated";
|
|
2
|
-
|
|
2
|
+
import type { TCarouselProps } from "../types";
|
|
3
|
+
export declare function handlerOffsetDirection(handlerOffset: SharedValue<number>, fixedDirection?: TCarouselProps["fixedDirection"]): -1 | 1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-reanimated-carousel",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.8",
|
|
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",
|
|
@@ -2,15 +2,10 @@ import React from "react";
|
|
|
2
2
|
import type { ViewStyle } from "react-native";
|
|
3
3
|
import type { AnimatedStyleProp } from "react-native-reanimated";
|
|
4
4
|
import Animated, {
|
|
5
|
-
runOnJS,
|
|
6
|
-
useAnimatedReaction,
|
|
7
5
|
useAnimatedStyle,
|
|
8
6
|
useDerivedValue,
|
|
9
7
|
} from "react-native-reanimated";
|
|
10
8
|
|
|
11
|
-
import { LazyView } from "./LazyView";
|
|
12
|
-
|
|
13
|
-
import { useCheckMounted } from "../hooks/useCheckMounted";
|
|
14
9
|
import type { IOpts } from "../hooks/useOffsetX";
|
|
15
10
|
import { useOffsetX } from "../hooks/useOffsetX";
|
|
16
11
|
import type { IVisibleRanges } from "../hooks/useVisibleRanges";
|
|
@@ -28,7 +23,6 @@ export const BaseLayout: React.FC<{
|
|
|
28
23
|
animationValue: Animated.SharedValue<number>
|
|
29
24
|
}) => React.ReactElement
|
|
30
25
|
}> = (props) => {
|
|
31
|
-
const mounted = useCheckMounted();
|
|
32
26
|
const { handlerOffset, index, children, visibleRanges, animationStyle }
|
|
33
27
|
= props;
|
|
34
28
|
|
|
@@ -46,7 +40,7 @@ export const BaseLayout: React.FC<{
|
|
|
46
40
|
},
|
|
47
41
|
} = context;
|
|
48
42
|
const size = vertical ? height : width;
|
|
49
|
-
|
|
43
|
+
|
|
50
44
|
let offsetXConfig: IOpts = {
|
|
51
45
|
handlerOffset,
|
|
52
46
|
index,
|
|
@@ -79,28 +73,6 @@ export const BaseLayout: React.FC<{
|
|
|
79
73
|
[animationStyle],
|
|
80
74
|
);
|
|
81
75
|
|
|
82
|
-
const updateView = React.useCallback(
|
|
83
|
-
(negativeRange: number[], positiveRange: number[]) => {
|
|
84
|
-
mounted.current
|
|
85
|
-
&& setShouldUpdate(
|
|
86
|
-
(index >= negativeRange[0] && index <= negativeRange[1])
|
|
87
|
-
|| (index >= positiveRange[0] && index <= positiveRange[1]),
|
|
88
|
-
);
|
|
89
|
-
},
|
|
90
|
-
[index, mounted],
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
useAnimatedReaction(
|
|
94
|
-
() => visibleRanges.value,
|
|
95
|
-
() => {
|
|
96
|
-
runOnJS(updateView)(
|
|
97
|
-
visibleRanges.value.negativeRange,
|
|
98
|
-
visibleRanges.value.positiveRange,
|
|
99
|
-
);
|
|
100
|
-
},
|
|
101
|
-
[visibleRanges.value],
|
|
102
|
-
);
|
|
103
|
-
|
|
104
76
|
return (
|
|
105
77
|
<Animated.View
|
|
106
78
|
style={[
|
|
@@ -116,11 +88,9 @@ export const BaseLayout: React.FC<{
|
|
|
116
88
|
* e.g.
|
|
117
89
|
* The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.
|
|
118
90
|
* */
|
|
119
|
-
testID={`__CAROUSEL_ITEM_${index}
|
|
91
|
+
testID={`__CAROUSEL_ITEM_${index}__`}
|
|
120
92
|
>
|
|
121
|
-
|
|
122
|
-
{children({ animationValue })}
|
|
123
|
-
</LazyView>
|
|
93
|
+
{children({ animationValue })}
|
|
124
94
|
</Animated.View>
|
|
125
95
|
);
|
|
126
96
|
};
|
|
@@ -3,7 +3,7 @@ import { StyleSheet } from "react-native";
|
|
|
3
3
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
4
4
|
import { runOnJS, useDerivedValue } from "react-native-reanimated";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ItemRenderer } from "./ItemRenderer";
|
|
7
7
|
import { ScrollViewGesture } from "./ScrollViewGesture";
|
|
8
8
|
|
|
9
9
|
import { useAutoPlay } from "../hooks/useAutoPlay";
|
|
@@ -13,7 +13,6 @@ import { useInitProps } from "../hooks/useInitProps";
|
|
|
13
13
|
import { useLayoutConfig } from "../hooks/useLayoutConfig";
|
|
14
14
|
import { useOnProgressChange } from "../hooks/useOnProgressChange";
|
|
15
15
|
import { usePropsErrorBoundary } from "../hooks/usePropsErrorBoundary";
|
|
16
|
-
import { useVisibleRanges } from "../hooks/useVisibleRanges";
|
|
17
16
|
import { CTX } from "../store";
|
|
18
17
|
import type { ICarouselInstance, TCarouselProps } from "../types";
|
|
19
18
|
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
@@ -30,8 +29,6 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
30
29
|
data,
|
|
31
30
|
// Length of fill data
|
|
32
31
|
dataLength,
|
|
33
|
-
// Raw data that has not been processed
|
|
34
|
-
rawData,
|
|
35
32
|
// Length of raw data
|
|
36
33
|
rawDataLength,
|
|
37
34
|
mode,
|
|
@@ -45,6 +42,7 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
45
42
|
autoPlayInterval,
|
|
46
43
|
scrollAnimationDuration,
|
|
47
44
|
withAnimation,
|
|
45
|
+
fixedDirection,
|
|
48
46
|
renderItem,
|
|
49
47
|
onScrollEnd,
|
|
50
48
|
onSnapToItem,
|
|
@@ -85,9 +83,10 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
85
83
|
handlerOffset,
|
|
86
84
|
withAnimation,
|
|
87
85
|
defaultIndex,
|
|
86
|
+
fixedDirection,
|
|
87
|
+
duration: scrollAnimationDuration,
|
|
88
88
|
onScrollEnd: () => runOnJS(_onScrollEnd)(),
|
|
89
89
|
onScrollBegin: () => !!onScrollBegin && runOnJS(onScrollBegin)(),
|
|
90
|
-
duration: scrollAnimationDuration,
|
|
91
90
|
});
|
|
92
91
|
|
|
93
92
|
const { next, prev, scrollTo, getSharedIndex, getCurrentIndex }
|
|
@@ -153,55 +152,8 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
153
152
|
[getCurrentIndex, next, prev, scrollTo],
|
|
154
153
|
);
|
|
155
154
|
|
|
156
|
-
const visibleRanges = useVisibleRanges({
|
|
157
|
-
total: dataLength,
|
|
158
|
-
viewSize: size,
|
|
159
|
-
translation: handlerOffset,
|
|
160
|
-
windowSize,
|
|
161
|
-
loop,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
155
|
const layoutConfig = useLayoutConfig({ ...props, size });
|
|
165
156
|
|
|
166
|
-
const renderLayout = React.useCallback(
|
|
167
|
-
(item: any, i: number) => {
|
|
168
|
-
const realIndex = computedRealIndexWithAutoFillData({
|
|
169
|
-
index: i,
|
|
170
|
-
dataLength: rawDataLength,
|
|
171
|
-
loop,
|
|
172
|
-
autoFillData,
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<BaseLayout
|
|
177
|
-
key={i}
|
|
178
|
-
index={i}
|
|
179
|
-
handlerOffset={offsetX}
|
|
180
|
-
visibleRanges={visibleRanges}
|
|
181
|
-
animationStyle={customAnimation || layoutConfig}
|
|
182
|
-
>
|
|
183
|
-
{({ animationValue }) =>
|
|
184
|
-
renderItem({
|
|
185
|
-
item,
|
|
186
|
-
index: realIndex,
|
|
187
|
-
animationValue,
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
</BaseLayout>
|
|
191
|
-
);
|
|
192
|
-
},
|
|
193
|
-
[
|
|
194
|
-
loop,
|
|
195
|
-
rawData,
|
|
196
|
-
offsetX,
|
|
197
|
-
visibleRanges,
|
|
198
|
-
autoFillData,
|
|
199
|
-
renderItem,
|
|
200
|
-
layoutConfig,
|
|
201
|
-
customAnimation,
|
|
202
|
-
],
|
|
203
|
-
);
|
|
204
|
-
|
|
205
157
|
return (
|
|
206
158
|
<GestureHandlerRootView>
|
|
207
159
|
<CTX.Provider value={{ props, common: commonVariables }}>
|
|
@@ -226,7 +178,20 @@ const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
|
|
|
226
178
|
onTouchBegin={scrollViewGestureOnTouchBegin}
|
|
227
179
|
onTouchEnd={scrollViewGestureOnTouchEnd}
|
|
228
180
|
>
|
|
229
|
-
|
|
181
|
+
<ItemRenderer
|
|
182
|
+
data={data}
|
|
183
|
+
dataLength={dataLength}
|
|
184
|
+
rawDataLength={rawDataLength}
|
|
185
|
+
loop={loop}
|
|
186
|
+
size={size}
|
|
187
|
+
windowSize={windowSize}
|
|
188
|
+
autoFillData={autoFillData}
|
|
189
|
+
offsetX={offsetX}
|
|
190
|
+
handlerOffset={handlerOffset}
|
|
191
|
+
layoutConfig={layoutConfig}
|
|
192
|
+
renderItem={renderItem}
|
|
193
|
+
customAnimation={customAnimation}
|
|
194
|
+
/>
|
|
230
195
|
</ScrollViewGesture>
|
|
231
196
|
</CTX.Provider>
|
|
232
197
|
</GestureHandlerRootView>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import type { ViewStyle } from "react-native";
|
|
4
|
+
import type Animated from "react-native-reanimated";
|
|
5
|
+
import { useAnimatedReaction, type AnimatedStyleProp, runOnJS } from "react-native-reanimated";
|
|
6
|
+
|
|
7
|
+
import type { TAnimationStyle } from "./BaseLayout";
|
|
8
|
+
import { BaseLayout } from "./BaseLayout";
|
|
9
|
+
|
|
10
|
+
import type { VisibleRanges } from "../hooks/useVisibleRanges";
|
|
11
|
+
import { useVisibleRanges } from "../hooks/useVisibleRanges";
|
|
12
|
+
import type { CarouselRenderItem } from "../types";
|
|
13
|
+
import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
data: any[]
|
|
17
|
+
dataLength: number
|
|
18
|
+
rawDataLength: number
|
|
19
|
+
loop: boolean
|
|
20
|
+
size: number
|
|
21
|
+
windowSize?: number
|
|
22
|
+
autoFillData: boolean
|
|
23
|
+
offsetX: Animated.SharedValue<number>
|
|
24
|
+
handlerOffset: Animated.SharedValue<number>
|
|
25
|
+
layoutConfig: TAnimationStyle
|
|
26
|
+
renderItem: CarouselRenderItem<any>
|
|
27
|
+
customAnimation?: ((value: number) => AnimatedStyleProp<ViewStyle>)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const ItemRenderer: FC<Props> = (props) => {
|
|
31
|
+
const {
|
|
32
|
+
data,
|
|
33
|
+
size,
|
|
34
|
+
windowSize,
|
|
35
|
+
handlerOffset,
|
|
36
|
+
offsetX,
|
|
37
|
+
dataLength,
|
|
38
|
+
rawDataLength,
|
|
39
|
+
loop,
|
|
40
|
+
autoFillData,
|
|
41
|
+
layoutConfig,
|
|
42
|
+
renderItem,
|
|
43
|
+
customAnimation,
|
|
44
|
+
} = props;
|
|
45
|
+
|
|
46
|
+
const visibleRanges = useVisibleRanges({
|
|
47
|
+
total: dataLength,
|
|
48
|
+
viewSize: size,
|
|
49
|
+
translation: handlerOffset,
|
|
50
|
+
windowSize,
|
|
51
|
+
loop,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const [displayedItems, setDisplayedItems] = React.useState<VisibleRanges>(null!);
|
|
55
|
+
|
|
56
|
+
useAnimatedReaction(
|
|
57
|
+
() => visibleRanges.value,
|
|
58
|
+
ranges => runOnJS(setDisplayedItems)(ranges),
|
|
59
|
+
[visibleRanges],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!displayedItems)
|
|
63
|
+
return null;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
{
|
|
68
|
+
data.map((item, index) => {
|
|
69
|
+
const realIndex = computedRealIndexWithAutoFillData({
|
|
70
|
+
index,
|
|
71
|
+
dataLength: rawDataLength,
|
|
72
|
+
loop,
|
|
73
|
+
autoFillData,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const { negativeRange, positiveRange } = displayedItems;
|
|
77
|
+
|
|
78
|
+
const shouldRender = (index >= negativeRange[0] && index <= negativeRange[1])
|
|
79
|
+
|| (index >= positiveRange[0] && index <= positiveRange[1]);
|
|
80
|
+
|
|
81
|
+
if (!shouldRender)
|
|
82
|
+
return null;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<BaseLayout
|
|
86
|
+
key={index}
|
|
87
|
+
index={index}
|
|
88
|
+
handlerOffset={offsetX}
|
|
89
|
+
visibleRanges={visibleRanges}
|
|
90
|
+
animationStyle={customAnimation || layoutConfig}
|
|
91
|
+
>
|
|
92
|
+
{({ animationValue }) =>
|
|
93
|
+
renderItem({
|
|
94
|
+
item,
|
|
95
|
+
index: realIndex,
|
|
96
|
+
animationValue,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
</BaseLayout>
|
|
100
|
+
);
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
</>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
@@ -21,9 +21,10 @@ interface IOpts {
|
|
|
21
21
|
loop: boolean
|
|
22
22
|
size: number
|
|
23
23
|
dataLength: number
|
|
24
|
-
autoFillData: TCarouselProps["autoFillData"]
|
|
25
24
|
handlerOffset: Animated.SharedValue<number>
|
|
25
|
+
autoFillData: TCarouselProps["autoFillData"]
|
|
26
26
|
withAnimation?: TCarouselProps["withAnimation"]
|
|
27
|
+
fixedDirection?: TCarouselProps["fixedDirection"]
|
|
27
28
|
duration?: number
|
|
28
29
|
defaultIndex?: number
|
|
29
30
|
onScrollBegin?: () => void
|
|
@@ -48,6 +49,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
48
49
|
defaultIndex = 0,
|
|
49
50
|
duration,
|
|
50
51
|
autoFillData,
|
|
52
|
+
fixedDirection,
|
|
51
53
|
} = options;
|
|
52
54
|
|
|
53
55
|
const dataInfo = React.useMemo(
|
|
@@ -241,7 +243,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
241
243
|
|
|
242
244
|
onScrollBegin?.();
|
|
243
245
|
// direction -> 1 | -1
|
|
244
|
-
const direction = handlerOffsetDirection(handlerOffset);
|
|
246
|
+
const direction = handlerOffsetDirection(handlerOffset, fixedDirection);
|
|
245
247
|
|
|
246
248
|
// target offset
|
|
247
249
|
const offset = i * size * direction;
|
|
@@ -252,16 +254,16 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
252
254
|
|
|
253
255
|
if (loop) {
|
|
254
256
|
isCloseToNextLoop
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
= Math.abs(handlerOffset.value % totalSize) / totalSize
|
|
258
|
+
>= 0.5;
|
|
257
259
|
}
|
|
258
260
|
|
|
259
261
|
const finalOffset
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
= (Math.floor(Math.abs(handlerOffset.value / totalSize))
|
|
263
|
+
+ (isCloseToNextLoop ? 1 : 0))
|
|
264
|
+
* totalSize
|
|
265
|
+
* direction
|
|
266
|
+
+ offset;
|
|
265
267
|
|
|
266
268
|
if (animated) {
|
|
267
269
|
index.value = i;
|
|
@@ -274,13 +276,14 @@ export function useCarouselController(options: IOpts): ICarouselController {
|
|
|
274
276
|
}
|
|
275
277
|
},
|
|
276
278
|
[
|
|
279
|
+
size,
|
|
280
|
+
loop,
|
|
277
281
|
index,
|
|
278
|
-
|
|
279
|
-
onScrollBegin,
|
|
282
|
+
fixedDirection,
|
|
280
283
|
handlerOffset,
|
|
281
|
-
size,
|
|
282
284
|
dataInfo.length,
|
|
283
|
-
|
|
285
|
+
canSliding,
|
|
286
|
+
onScrollBegin,
|
|
284
287
|
scrollWithTiming,
|
|
285
288
|
],
|
|
286
289
|
);
|