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.
Files changed (44) hide show
  1. package/lib/commonjs/components/BaseLayout.js +2 -20
  2. package/lib/commonjs/components/BaseLayout.js.map +1 -1
  3. package/lib/commonjs/components/Carousel.js +19 -41
  4. package/lib/commonjs/components/Carousel.js.map +1 -1
  5. package/lib/commonjs/components/ItemRenderer.js +80 -0
  6. package/lib/commonjs/components/ItemRenderer.js.map +1 -0
  7. package/lib/commonjs/hooks/useCarouselController.js +4 -3
  8. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  9. package/lib/commonjs/hooks/useOffsetX.test.js.map +1 -1
  10. package/lib/commonjs/hooks/useVisibleRanges.js +36 -18
  11. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  12. package/lib/commonjs/utils/handleroffset-direction.js +5 -5
  13. package/lib/commonjs/utils/handleroffset-direction.js.map +1 -1
  14. package/lib/commonjs/utils/handleroffset-direction.test.js +46 -0
  15. package/lib/commonjs/utils/handleroffset-direction.test.js.map +1 -0
  16. package/lib/module/components/BaseLayout.js +3 -15
  17. package/lib/module/components/BaseLayout.js.map +1 -1
  18. package/lib/module/components/Carousel.js +19 -38
  19. package/lib/module/components/Carousel.js.map +1 -1
  20. package/lib/module/components/ItemRenderer.js +62 -0
  21. package/lib/module/components/ItemRenderer.js.map +1 -0
  22. package/lib/module/hooks/useCarouselController.js +4 -3
  23. package/lib/module/hooks/useCarouselController.js.map +1 -1
  24. package/lib/module/hooks/useOffsetX.test.js.map +1 -1
  25. package/lib/module/hooks/useVisibleRanges.js +35 -18
  26. package/lib/module/hooks/useVisibleRanges.js.map +1 -1
  27. package/lib/module/utils/handleroffset-direction.js +5 -5
  28. package/lib/module/utils/handleroffset-direction.js.map +1 -1
  29. package/lib/module/utils/handleroffset-direction.test.js +41 -0
  30. package/lib/module/utils/handleroffset-direction.test.js.map +1 -0
  31. package/lib/typescript/components/ItemRenderer.d.ts +22 -0
  32. package/lib/typescript/hooks/useCarouselController.d.ts +2 -1
  33. package/lib/typescript/hooks/useVisibleRanges.d.ts +7 -4
  34. package/lib/typescript/utils/handleroffset-direction.d.ts +2 -1
  35. package/lib/typescript/utils/handleroffset-direction.test.d.ts +1 -0
  36. package/package.json +1 -1
  37. package/src/components/BaseLayout.tsx +3 -33
  38. package/src/components/Carousel.tsx +18 -53
  39. package/src/components/ItemRenderer.tsx +105 -0
  40. package/src/hooks/useCarouselController.tsx +16 -13
  41. package/src/hooks/useOffsetX.test.ts +1 -1
  42. package/src/hooks/useVisibleRanges.tsx +56 -24
  43. package/src/utils/handleroffset-direction.test.ts +52 -0
  44. 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":"AACA,SAASA,eAAT,QAAgC,yBAAhC;AAOA,OAAO,SAASC,gBAAT,CAA0BC,OAA1B,EAMY;AACjB,QAAM;AACJC,IAAAA,KAAK,GAAG,CADJ;AAEJC,IAAAA,QAFI;AAGJC,IAAAA,WAHI;AAIJC,IAAAA,UAAU,EAAEC,WAAW,GAAG,CAJtB;AAKJC,IAAAA;AALI,MAMFN,OANJ;AAQA,QAAMI,UAAU,GAAGH,KAAK,IAAII,WAAT,GAAuBJ,KAAvB,GAA+BI,WAAlD;AAEA,QAAME,MAAM,GAAGT,eAAe,CAAC,MAAM;AACnC,UAAMU,aAAa,GAAGC,IAAI,CAACC,KAAL,CAAWN,UAAU,GAAG,CAAxB,CAAtB;AACA,UAAMO,aAAa,GAAGP,UAAU,GAAGI,aAAnC;AAEA,QAAII,YAAY,GAAGH,IAAI,CAACC,KAAL,CAAW,CAACP,WAAW,CAACU,KAAb,GAAqBX,QAAhC,CAAnB;AACAU,IAAAA,YAAY,GAAGA,YAAY,GAAG,CAAf,GAAoBA,YAAY,GAAGX,KAAhB,GAAyBA,KAA5C,GAAoDW,YAAnE;;AAEA,QAAI,CAACN,IAAL,EAAW;AACT;AACA;AACA,aAAO;AACLQ,QAAAA,aAAa,EAAE,CAAC,IAAIF,YAAJ,IAAoBR,UAAU,GAAG,CAAjC,CAAD,EAAsC,IAAIQ,YAA1C,CADV;AAELG,QAAAA,aAAa,EAAE,CAAC,IAAIH,YAAL,EAAmBR,UAAU,GAAG,CAAb,GAAiBQ,YAApC;AAFV,OAAP;AAID;;AAED,UAAME,aAAa,GAAG,CACpB,CAACF,YAAY,GAAGD,aAAf,GAA+BV,KAAhC,IAAyCA,KADrB,EAEpB,CAACW,YAAY,GAAG,CAAf,GAAmBX,KAApB,IAA6BA,KAFT,CAAtB;AAKA,UAAMc,aAAa,GAAG,CACpB,CAACH,YAAY,GAAGX,KAAhB,IAAyBA,KADL,EAEpB,CAACW,YAAY,GAAGJ,aAAf,GAA+BP,KAAhC,IAAyCA,KAFrB,CAAtB;;AAKA,QAAIa,aAAa,CAAC,CAAD,CAAb,GAAmBb,KAAnB,IAA4Ba,aAAa,CAAC,CAAD,CAAb,GAAmBA,aAAa,CAAC,CAAD,CAAhE,EAAqE;AACnEA,MAAAA,aAAa,CAAC,CAAD,CAAb,GAAmBb,KAAK,GAAG,CAA3B;AACAc,MAAAA,aAAa,CAAC,CAAD,CAAb,GAAmB,CAAnB;AACD;;AACD,QAAIA,aAAa,CAAC,CAAD,CAAb,GAAmBA,aAAa,CAAC,CAAD,CAApC,EAAyC;AACvCD,MAAAA,aAAa,CAAC,CAAD,CAAb,GAAmBb,KAAK,GAAG,CAA3B;AACAc,MAAAA,aAAa,CAAC,CAAD,CAAb,GAAmB,CAAnB;AACD;;AAED,WAAO;AAAED,MAAAA,aAAF;AAAiBC,MAAAA;AAAjB,KAAP;AACD,GApC6B,EAoC3B,CAACT,IAAD,EAAOL,KAAP,EAAcG,UAAd,EAA0BD,WAA1B,CApC2B,CAA9B;AAsCA,SAAOI,MAAP;AACD","sourcesContent":["import type Animated from \"react-native-reanimated\";\nimport { useDerivedValue } from \"react-native-reanimated\";\n\nexport type IVisibleRanges = Animated.SharedValue<{\n negativeRange: number[]\n positiveRange: number[]\n}>;\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 = 0,\n loop,\n } = options;\n\n const windowSize = total <= _windowSize ? total : _windowSize;\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 if (!loop) {\n // Adjusting negative range if the carousel is not loopable.\n // So, It will be only displayed the positive items.\n return {\n negativeRange: [0 + currentIndex - (windowSize - 1), 0 + currentIndex],\n positiveRange: [0 + currentIndex, windowSize - 1 + currentIndex],\n };\n }\n\n const negativeRange = [\n (currentIndex - negativeCount + total) % total,\n (currentIndex - 1 + total) % total,\n ];\n\n const positiveRange = [\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 return { negativeRange, positiveRange };\n }, [loop, total, windowSize, translation]);\n\n return ranges;\n}\n"]}
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
- const isPositiveZero = Object.is(handlerOffset.value, +0);
5
- const isNegativeZero = Object.is(handlerOffset.value, -0);
6
- const direction = isPositiveZero ? 1 : isNegativeZero ? -1 : Math.sign(handlerOffset.value);
7
- return direction;
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","isPositiveZero","Object","is","value","isNegativeZero","direction","Math","sign"],"mappings":"AAEA,OAAO,SAASA,sBAAT,CAAgCC,aAAhC,EAA4E;AACjF;;AAEA,QAAMC,cAAc,GAAGC,MAAM,CAACC,EAAP,CAAUH,aAAa,CAACI,KAAxB,EAA+B,CAAC,CAAhC,CAAvB;AACA,QAAMC,cAAc,GAAGH,MAAM,CAACC,EAAP,CAAUH,aAAa,CAACI,KAAxB,EAA+B,CAAC,CAAhC,CAAvB;AACA,QAAME,SAAS,GAAGL,cAAc,GAC5B,CAD4B,GAE5BI,cAAc,GACZ,CAAC,CADW,GAEZE,IAAI,CAACC,IAAL,CAAUR,aAAa,CAACI,KAAxB,CAJN;AAMA,SAAOE,SAAP;AACD","sourcesContent":["import type { SharedValue } from \"react-native-reanimated\";\n\nexport function handlerOffsetDirection(handlerOffset: SharedValue<number>): -1 | 1 {\n \"worklet\";\n\n const isPositiveZero = Object.is(handlerOffset.value, +0);\n const isNegativeZero = Object.is(handlerOffset.value, -0);\n const direction = isPositiveZero\n ? 1\n : isNegativeZero\n ? -1\n : Math.sign(handlerOffset.value) as -1 | 1;\n\n return direction;\n}\n"]}
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
- export declare type IVisibleRanges = Animated.SharedValue<{
3
- negativeRange: number[];
4
- positiveRange: number[];
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
- export declare function handlerOffsetDirection(handlerOffset: SharedValue<number>): -1 | 1;
2
+ import type { TCarouselProps } from "../types";
3
+ export declare function handlerOffsetDirection(handlerOffset: SharedValue<number>, fixedDirection?: TCarouselProps["fixedDirection"]): -1 | 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reanimated-carousel",
3
- "version": "4.0.0-alpha.6",
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
- const [shouldUpdate, setShouldUpdate] = React.useState(false);
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}_${shouldUpdate ? "READY" : "NOT_READY"}__`}
91
+ testID={`__CAROUSEL_ITEM_${index}__`}
120
92
  >
121
- <LazyView shouldUpdate={shouldUpdate}>
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 { BaseLayout } from "./BaseLayout";
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
- {data.map(renderLayout)}
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
- = Math.abs(handlerOffset.value % totalSize) / totalSize
256
- >= 0.5;
257
+ = Math.abs(handlerOffset.value % totalSize) / totalSize
258
+ >= 0.5;
257
259
  }
258
260
 
259
261
  const finalOffset
260
- = (Math.floor(Math.abs(handlerOffset.value / totalSize))
261
- + (isCloseToNextLoop ? 1 : 0))
262
- * totalSize
263
- * direction
264
- + offset;
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
- canSliding,
279
- onScrollBegin,
282
+ fixedDirection,
280
283
  handlerOffset,
281
- size,
282
284
  dataInfo.length,
283
- loop,
285
+ canSliding,
286
+ onScrollBegin,
284
287
  scrollWithTiming,
285
288
  ],
286
289
  );
@@ -12,7 +12,7 @@ describe("useSharedValue", () => {
12
12
  const range = useSharedValue({
13
13
  negativeRange: [7, 9],
14
14
  positiveRange: [0, 3],
15
- });
15
+ }) as IVisibleRanges;
16
16
  const inputs: Array<{
17
17
  config: IOpts
18
18
  range: IVisibleRanges