react-native-reanimated-carousel 3.1.5 → 3.3.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.
Files changed (56) hide show
  1. package/README.md +9 -0
  2. package/README.zh-CN.md +38 -30
  3. package/lib/commonjs/Carousel.js +1 -1
  4. package/lib/commonjs/Carousel.js.map +1 -1
  5. package/lib/commonjs/ScrollViewGesture.js +1 -1
  6. package/lib/commonjs/ScrollViewGesture.js.map +1 -1
  7. package/lib/commonjs/hooks/computeNewIndexWhenDataChanges.js +1 -1
  8. package/lib/commonjs/hooks/computeNewIndexWhenDataChanges.js.map +1 -1
  9. package/lib/commonjs/hooks/index.test.js +1 -1
  10. package/lib/commonjs/hooks/index.test.js.map +1 -1
  11. package/lib/commonjs/hooks/useCarouselController.js +1 -1
  12. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  13. package/lib/commonjs/hooks/useCommonVariables.js +1 -1
  14. package/lib/commonjs/hooks/useCommonVariables.js.map +1 -1
  15. package/lib/commonjs/hooks/useInitProps.js +1 -1
  16. package/lib/commonjs/hooks/useInitProps.js.map +1 -1
  17. package/lib/commonjs/hooks/useOffsetX.js +1 -1
  18. package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
  19. package/lib/commonjs/hooks/useOnProgressChange.js +1 -1
  20. package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -1
  21. package/lib/commonjs/hooks/useVisibleRanges.js +1 -1
  22. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  23. package/lib/commonjs/layouts/BaseLayout.js +1 -1
  24. package/lib/commonjs/layouts/BaseLayout.js.map +1 -1
  25. package/lib/commonjs/layouts/ParallaxLayout.js +1 -1
  26. package/lib/commonjs/layouts/ParallaxLayout.js.map +1 -1
  27. package/lib/commonjs/layouts/normal.js +1 -1
  28. package/lib/commonjs/layouts/normal.js.map +1 -1
  29. package/lib/commonjs/layouts/parallax.js +1 -1
  30. package/lib/commonjs/layouts/parallax.js.map +1 -1
  31. package/lib/commonjs/layouts/stack.js +1 -1
  32. package/lib/commonjs/layouts/stack.js.map +1 -1
  33. package/lib/commonjs/utils/computedWithAutoFillData.js +1 -1
  34. package/lib/commonjs/utils/computedWithAutoFillData.js.map +1 -1
  35. package/lib/commonjs/utils/dealWithAnimation.js +1 -1
  36. package/lib/commonjs/utils/dealWithAnimation.js.map +1 -1
  37. package/lib/commonjs/utils/handlerOffsetDirection.js +1 -1
  38. package/lib/commonjs/utils/log.js +1 -1
  39. package/lib/module/ScrollViewGesture.js +79 -37
  40. package/lib/module/ScrollViewGesture.js.map +1 -1
  41. package/lib/module/hooks/index.test.js +0 -1
  42. package/lib/module/hooks/index.test.js.map +1 -1
  43. package/lib/module/hooks/useInitProps.js +5 -2
  44. package/lib/module/hooks/useInitProps.js.map +1 -1
  45. package/lib/module/layouts/BaseLayout.js +7 -0
  46. package/lib/module/layouts/BaseLayout.js.map +1 -1
  47. package/lib/module/utils/dealWithAnimation.js +2 -6
  48. package/lib/module/utils/dealWithAnimation.js.map +1 -1
  49. package/lib/typescript/types.d.ts +10 -5
  50. package/package.json +13 -10
  51. package/src/ScrollViewGesture.tsx +80 -39
  52. package/src/hooks/index.test.ts +0 -2
  53. package/src/hooks/useInitProps.ts +5 -2
  54. package/src/layouts/BaseLayout.tsx +7 -1
  55. package/src/types.ts +12 -5
  56. package/src/utils/dealWithAnimation.ts +6 -10
@@ -1 +1 @@
1
- {"version":3,"sources":["useInitProps.ts"],"names":["React","computedFillDataWithAutoFillData","useInitProps","props","defaultIndex","data","rawData","loop","enabled","autoPlayInterval","_autoPlayInterval","scrollAnimationDuration","style","panGestureHandlerProps","pagingEnabled","autoFillData","snapEnabled","enableSnap","width","_width","height","_height","Math","round","max","useMemo","dataLength","length","rawDataLength","mode","modeConfig","showLength"],"mappings":"AAAA,OAAOA,KAAP,MAAkB,OAAlB;AAGA,SAASC,gCAAT,QAAiD,mCAAjD;AAuBA,OAAO,SAASC,YAAT,CACLC,KADK,EAEwB;AAAA;;AAC7B,QAAM;AACJC,IAAAA,YAAY,GAAG,CADX;AAEJC,IAAAA,IAAI,EAAEC,OAAO,GAAG,EAFZ;AAGJC,IAAAA,IAAI,GAAG,IAHH;AAIJC,IAAAA,OAAO,GAAG,IAJN;AAKJC,IAAAA,gBAAgB,EAAEC,iBAAiB,GAAG,IALlC;AAMJC,IAAAA,uBAAuB,GAAG,GANtB;AAOJC,IAAAA,KAAK,GAAG,EAPJ;AAQJC,IAAAA,sBAAsB,GAAG,EARrB;AASJC,IAAAA,aAAa,GAAG,IATZ;AAUJC,IAAAA,YAAY,GAAG,IAVX;AAWJC,IAAAA,WAAW,wBAAGb,KAAK,CAACc,UAAT,iEAAuB,IAX9B;AAYJC,IAAAA,KAAK,EAAEC,MAZH;AAaJC,IAAAA,MAAM,EAAEC;AAbJ,MAcFlB,KAdJ;AAgBA,QAAMe,KAAK,GAAGI,IAAI,CAACC,KAAL,CAAWJ,MAAM,IAAI,CAArB,CAAd;AACA,QAAMC,MAAM,GAAGE,IAAI,CAACC,KAAL,CAAWF,OAAO,IAAI,CAAtB,CAAf;AACA,QAAMZ,gBAAgB,GAAGa,IAAI,CAACE,GAAL,CAASd,iBAAT,EAA4B,CAA5B,CAAzB;AAEA,QAAML,IAAI,GAAGL,KAAK,CAACyB,OAAN,CACX,MAAM;AACJ,WAAOxB,gCAAgC,CAAI;AACzCM,MAAAA,IADyC;AAEzCQ,MAAAA,YAFyC;AAGzCV,MAAAA,IAAI,EAAEC,OAHmC;AAIzCoB,MAAAA,UAAU,EAAEpB,OAAO,CAACqB;AAJqB,KAAJ,CAAvC;AAMD,GARU,EASX,CAACrB,OAAD,EAAUC,IAAV,EAAgBQ,YAAhB,CATW,CAAb;AAYA,QAAMW,UAAU,GAAGrB,IAAI,CAACsB,MAAxB;AACA,QAAMC,aAAa,GAAGtB,OAAO,CAACqB,MAA9B;;AAEA,MAAIxB,KAAK,CAAC0B,IAAN,KAAe,gBAAf,IAAmC1B,KAAK,CAAC0B,IAAN,KAAe,kBAAtD,EAA0E;AAAA;;AACxE,QAAI,CAAC1B,KAAK,CAAC2B,UAAX,EACE3B,KAAK,CAAC2B,UAAN,GAAmB,EAAnB;AAEF3B,IAAAA,KAAK,CAAC2B,UAAN,CAAiBC,UAAjB,iDAA8B5B,KAAK,CAAC2B,UAApC,sDAA8B,kBAAkBC,UAAhD,yEAA8DL,UAAU,GAAG,CAA3E;AACD;;AAED,SAAO,EACL,GAAGvB,KADE;AAELC,IAAAA,YAFK;AAGLW,IAAAA,YAHK;AAIL;AACAV,IAAAA,IALK;AAML;AACAqB,IAAAA,UAPK;AAQL;AACApB,IAAAA,OATK;AAUL;AACAsB,IAAAA,aAXK;AAYLrB,IAAAA,IAZK;AAaLC,IAAAA,OAbK;AAcLC,IAAAA,gBAdK;AAeLE,IAAAA,uBAfK;AAgBLC,IAAAA,KAhBK;AAiBLC,IAAAA,sBAjBK;AAkBLC,IAAAA,aAlBK;AAmBLE,IAAAA,WAnBK;AAoBLE,IAAAA,KApBK;AAqBLE,IAAAA;AArBK,GAAP;AAuBD","sourcesContent":["import React from \"react\";\n\nimport type { TCarouselProps } from \"../types\";\nimport { computedFillDataWithAutoFillData } from \"../utils/computedWithAutoFillData\";\n\ntype TGetRequiredProps<P extends keyof TCarouselProps> = Record<\nP,\nRequired<TCarouselProps>[P]\n>;\n\nexport type TInitializeCarouselProps<T> = TCarouselProps<T> &\nTGetRequiredProps<\n| \"defaultIndex\"\n| \"loop\"\n| \"width\"\n| \"height\"\n| \"scrollAnimationDuration\"\n| \"autoPlayInterval\"\n| \"autoFillData\"\n> & {\n // Raw data that has not been processed\n rawData: T[]\n dataLength: number\n rawDataLength: number\n};\n\nexport function useInitProps<T>(\n props: TCarouselProps<T>,\n): TInitializeCarouselProps<T> {\n const {\n defaultIndex = 0,\n data: rawData = [],\n loop = true,\n enabled = true,\n autoPlayInterval: _autoPlayInterval = 1000,\n scrollAnimationDuration = 500,\n style = {},\n panGestureHandlerProps = {},\n pagingEnabled = true,\n autoFillData = true,\n snapEnabled = props.enableSnap ?? true,\n width: _width,\n height: _height,\n } = props;\n\n const width = Math.round(_width || 0);\n const height = Math.round(_height || 0);\n const autoPlayInterval = Math.max(_autoPlayInterval, 0);\n\n const data = React.useMemo<T[]>(\n () => {\n return computedFillDataWithAutoFillData<T>({\n loop,\n autoFillData,\n data: rawData,\n dataLength: rawData.length,\n });\n },\n [rawData, loop, autoFillData],\n );\n\n const dataLength = data.length;\n const rawDataLength = rawData.length;\n\n if (props.mode === \"vertical-stack\" || props.mode === \"horizontal-stack\") {\n if (!props.modeConfig)\n props.modeConfig = {};\n\n props.modeConfig.showLength = props.modeConfig?.showLength ?? dataLength - 1;\n }\n\n return {\n ...props,\n defaultIndex,\n autoFillData,\n // Fill data with autoFillData\n data,\n // Length of fill data\n dataLength,\n // Raw data that has not been processed\n rawData,\n // Length of raw data\n rawDataLength,\n loop,\n enabled,\n autoPlayInterval,\n scrollAnimationDuration,\n style,\n panGestureHandlerProps,\n pagingEnabled,\n snapEnabled,\n width,\n height,\n };\n}\n"]}
1
+ {"version":3,"sources":["useInitProps.ts"],"names":["React","computedFillDataWithAutoFillData","useInitProps","props","defaultIndex","data","rawData","loop","autoPlayInterval","_autoPlayInterval","scrollAnimationDuration","style","panGestureHandlerProps","autoFillData","enabled","pagingEnabled","overscrollEnabled","snapEnabled","enableSnap","width","_width","height","_height","Math","round","max","useMemo","dataLength","length","rawDataLength","mode","modeConfig","showLength"],"mappings":"AAAA,OAAOA,KAAP,MAAkB,OAAlB;AAGA,SAASC,gCAAT,QAAiD,mCAAjD;AAuBA,OAAO,SAASC,YAAT,CACLC,KADK,EAEwB;AAAA;;AAC7B,QAAM;AACJC,IAAAA,YAAY,GAAG,CADX;AAEJC,IAAAA,IAAI,EAAEC,OAAO,GAAG,EAFZ;AAGJC,IAAAA,IAAI,GAAG,IAHH;AAIJC,IAAAA,gBAAgB,EAAEC,iBAAiB,GAAG,IAJlC;AAKJC,IAAAA,uBAAuB,GAAG,GALtB;AAMJC,IAAAA,KAAK,GAAG,EANJ;AAOJC,IAAAA,sBAAsB,GAAG,EAPrB;AAQJC,IAAAA,YAAY,GAAG,IARX;AASJ;AACAC,IAAAA,OAAO,GAAG,IAVN;AAWJC,IAAAA,aAAa,GAAG,IAXZ;AAYJC,IAAAA,iBAAiB,GAAG,IAZhB;AAaJC,IAAAA,WAAW,wBAAGd,KAAK,CAACe,UAAT,iEAAuB,IAb9B;AAcJC,IAAAA,KAAK,EAAEC,MAdH;AAeJC,IAAAA,MAAM,EAAEC;AAfJ,MAgBFnB,KAhBJ;AAkBA,QAAMgB,KAAK,GAAGI,IAAI,CAACC,KAAL,CAAWJ,MAAM,IAAI,CAArB,CAAd;AACA,QAAMC,MAAM,GAAGE,IAAI,CAACC,KAAL,CAAWF,OAAO,IAAI,CAAtB,CAAf;AACA,QAAMd,gBAAgB,GAAGe,IAAI,CAACE,GAAL,CAAShB,iBAAT,EAA4B,CAA5B,CAAzB;AAEA,QAAMJ,IAAI,GAAGL,KAAK,CAAC0B,OAAN,CACX,MAAM;AACJ,WAAOzB,gCAAgC,CAAI;AACzCM,MAAAA,IADyC;AAEzCM,MAAAA,YAFyC;AAGzCR,MAAAA,IAAI,EAAEC,OAHmC;AAIzCqB,MAAAA,UAAU,EAAErB,OAAO,CAACsB;AAJqB,KAAJ,CAAvC;AAMD,GARU,EASX,CAACtB,OAAD,EAAUC,IAAV,EAAgBM,YAAhB,CATW,CAAb;AAYA,QAAMc,UAAU,GAAGtB,IAAI,CAACuB,MAAxB;AACA,QAAMC,aAAa,GAAGvB,OAAO,CAACsB,MAA9B;;AAEA,MAAIzB,KAAK,CAAC2B,IAAN,KAAe,gBAAf,IAAmC3B,KAAK,CAAC2B,IAAN,KAAe,kBAAtD,EAA0E;AAAA;;AACxE,QAAI,CAAC3B,KAAK,CAAC4B,UAAX,EACE5B,KAAK,CAAC4B,UAAN,GAAmB,EAAnB;AAEF5B,IAAAA,KAAK,CAAC4B,UAAN,CAAiBC,UAAjB,iDAA8B7B,KAAK,CAAC4B,UAApC,sDAA8B,kBAAkBC,UAAhD,yEAA8DL,UAAU,GAAG,CAA3E;AACD;;AAED,SAAO,EACL,GAAGxB,KADE;AAELC,IAAAA,YAFK;AAGLS,IAAAA,YAHK;AAIL;AACAR,IAAAA,IALK;AAML;AACAsB,IAAAA,UAPK;AAQL;AACArB,IAAAA,OATK;AAUL;AACAuB,IAAAA,aAXK;AAYLtB,IAAAA,IAZK;AAaLO,IAAAA,OAbK;AAcLN,IAAAA,gBAdK;AAeLE,IAAAA,uBAfK;AAgBLC,IAAAA,KAhBK;AAiBLC,IAAAA,sBAjBK;AAkBLG,IAAAA,aAlBK;AAmBLE,IAAAA,WAnBK;AAoBLD,IAAAA,iBApBK;AAqBLG,IAAAA,KArBK;AAsBLE,IAAAA;AAtBK,GAAP;AAwBD","sourcesContent":["import React from \"react\";\n\nimport type { TCarouselProps } from \"../types\";\nimport { computedFillDataWithAutoFillData } from \"../utils/computedWithAutoFillData\";\n\ntype TGetRequiredProps<P extends keyof TCarouselProps> = Record<\nP,\nRequired<TCarouselProps>[P]\n>;\n\nexport type TInitializeCarouselProps<T> = TCarouselProps<T> &\nTGetRequiredProps<\n| \"defaultIndex\"\n| \"loop\"\n| \"width\"\n| \"height\"\n| \"scrollAnimationDuration\"\n| \"autoPlayInterval\"\n| \"autoFillData\"\n> & {\n // Raw data that has not been processed\n rawData: T[]\n dataLength: number\n rawDataLength: number\n};\n\nexport function useInitProps<T>(\n props: TCarouselProps<T>,\n): TInitializeCarouselProps<T> {\n const {\n defaultIndex = 0,\n data: rawData = [],\n loop = true,\n autoPlayInterval: _autoPlayInterval = 1000,\n scrollAnimationDuration = 500,\n style = {},\n panGestureHandlerProps = {},\n autoFillData = true,\n // switchers\n enabled = true,\n pagingEnabled = true,\n overscrollEnabled = true,\n snapEnabled = props.enableSnap ?? true,\n width: _width,\n height: _height,\n } = props;\n\n const width = Math.round(_width || 0);\n const height = Math.round(_height || 0);\n const autoPlayInterval = Math.max(_autoPlayInterval, 0);\n\n const data = React.useMemo<T[]>(\n () => {\n return computedFillDataWithAutoFillData<T>({\n loop,\n autoFillData,\n data: rawData,\n dataLength: rawData.length,\n });\n },\n [rawData, loop, autoFillData],\n );\n\n const dataLength = data.length;\n const rawDataLength = rawData.length;\n\n if (props.mode === \"vertical-stack\" || props.mode === \"horizontal-stack\") {\n if (!props.modeConfig)\n props.modeConfig = {};\n\n props.modeConfig.showLength = props.modeConfig?.showLength ?? dataLength - 1;\n }\n\n return {\n ...props,\n defaultIndex,\n autoFillData,\n // Fill data with autoFillData\n data,\n // Length of fill data\n dataLength,\n // Raw data that has not been processed\n rawData,\n // Length of raw data\n rawDataLength,\n loop,\n enabled,\n autoPlayInterval,\n scrollAnimationDuration,\n style,\n panGestureHandlerProps,\n pagingEnabled,\n snapEnabled,\n overscrollEnabled,\n width,\n height,\n };\n}\n"]}
@@ -68,6 +68,13 @@ export const BaseLayout = props => {
68
68
  height: height || "100%",
69
69
  position: "absolute"
70
70
  }, animatedStyle]
71
+ /**
72
+ * We use this testID to know when the carousel item is ready to be tested in test.
73
+ * e.g.
74
+ * The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.
75
+ * */
76
+ ,
77
+ testID: `__CAROUSEL_ITEM_${index}_${shouldUpdate ? "READY" : "NOT_READY"}__`
71
78
  }, /*#__PURE__*/React.createElement(LazyView, {
72
79
  shouldUpdate: shouldUpdate
73
80
  }, children({
@@ -1 +1 @@
1
- {"version":3,"sources":["BaseLayout.tsx"],"names":["React","Animated","runOnJS","useAnimatedReaction","useAnimatedStyle","useDerivedValue","useCheckMounted","useOffsetX","LazyView","CTX","BaseLayout","props","mounted","handlerOffset","index","children","visibleRanges","animationStyle","context","useContext","loop","dataLength","width","height","vertical","customConfig","mode","modeConfig","size","shouldUpdate","setShouldUpdate","useState","offsetXConfig","snapDirection","showLength","type","viewCount","x","animationValue","value","animatedStyle","updateView","useCallback","negativeRange","positiveRange","current","position"],"mappings":"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","sourcesContent":["import React from \"react\";\nimport type { ViewStyle } from \"react-native\";\nimport type { AnimatedStyleProp } from \"react-native-reanimated\";\nimport Animated, {\n runOnJS,\n useAnimatedReaction,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\n\nimport type { ILayoutConfig } from \"./stack\";\n\nimport { useCheckMounted } from \"../hooks/useCheckMounted\";\nimport type { IOpts } from \"../hooks/useOffsetX\";\nimport { useOffsetX } from \"../hooks/useOffsetX\";\nimport type { IVisibleRanges } from \"../hooks/useVisibleRanges\";\nimport { LazyView } from \"../LazyView\";\nimport { CTX } from \"../store\";\n\nexport type TAnimationStyle = (value: number) => AnimatedStyleProp<ViewStyle>;\n\nexport const BaseLayout: React.FC<{\n index: number\n handlerOffset: Animated.SharedValue<number>\n visibleRanges: IVisibleRanges\n animationStyle: TAnimationStyle\n children: (ctx: {\n animationValue: Animated.SharedValue<number>\n }) => React.ReactElement\n}> = (props) => {\n const mounted = useCheckMounted();\n const { handlerOffset, index, children, visibleRanges, animationStyle }\n = props;\n\n const context = React.useContext(CTX);\n const {\n props: {\n loop,\n dataLength,\n width,\n height,\n vertical,\n customConfig,\n mode,\n modeConfig,\n },\n } = context;\n const size = vertical ? height : width;\n const [shouldUpdate, setShouldUpdate] = React.useState(false);\n let offsetXConfig: IOpts = {\n handlerOffset,\n index,\n size,\n dataLength,\n loop,\n ...(typeof customConfig === \"function\" ? customConfig() : {}),\n };\n\n if (mode === \"horizontal-stack\") {\n const { snapDirection, showLength } = modeConfig as ILayoutConfig;\n\n offsetXConfig = {\n handlerOffset,\n index,\n size,\n dataLength,\n loop,\n type: snapDirection === \"right\" ? \"negative\" : \"positive\",\n viewCount: showLength,\n };\n }\n\n const x = useOffsetX(offsetXConfig, visibleRanges);\n const animationValue = useDerivedValue(() => x.value / size, [x, size]);\n const animatedStyle = useAnimatedStyle(\n () => animationStyle(x.value / size),\n [animationStyle],\n );\n\n const updateView = React.useCallback(\n (negativeRange: number[], positiveRange: number[]) => {\n mounted.current\n && setShouldUpdate(\n (index >= negativeRange[0] && index <= negativeRange[1])\n || (index >= positiveRange[0] && index <= positiveRange[1]),\n );\n },\n [index, mounted],\n );\n\n useAnimatedReaction(\n () => visibleRanges.value,\n () => {\n runOnJS(updateView)(\n visibleRanges.value.negativeRange,\n visibleRanges.value.positiveRange,\n );\n },\n [visibleRanges.value],\n );\n\n return (\n <Animated.View\n style={[\n {\n width: width || \"100%\",\n height: height || \"100%\",\n position: \"absolute\",\n },\n animatedStyle,\n ]}\n >\n <LazyView shouldUpdate={shouldUpdate}>\n {children({ animationValue })}\n </LazyView>\n </Animated.View>\n );\n};\n"]}
1
+ {"version":3,"sources":["BaseLayout.tsx"],"names":["React","Animated","runOnJS","useAnimatedReaction","useAnimatedStyle","useDerivedValue","useCheckMounted","useOffsetX","LazyView","CTX","BaseLayout","props","mounted","handlerOffset","index","children","visibleRanges","animationStyle","context","useContext","loop","dataLength","width","height","vertical","customConfig","mode","modeConfig","size","shouldUpdate","setShouldUpdate","useState","offsetXConfig","snapDirection","showLength","type","viewCount","x","animationValue","value","animatedStyle","updateView","useCallback","negativeRange","positiveRange","current","position"],"mappings":"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","sourcesContent":["import React from \"react\";\nimport type { ViewStyle } from \"react-native\";\nimport type { AnimatedStyleProp } from \"react-native-reanimated\";\nimport Animated, {\n runOnJS,\n useAnimatedReaction,\n useAnimatedStyle,\n useDerivedValue,\n} from \"react-native-reanimated\";\n\nimport type { ILayoutConfig } from \"./stack\";\n\nimport { useCheckMounted } from \"../hooks/useCheckMounted\";\nimport type { IOpts } from \"../hooks/useOffsetX\";\nimport { useOffsetX } from \"../hooks/useOffsetX\";\nimport type { IVisibleRanges } from \"../hooks/useVisibleRanges\";\nimport { LazyView } from \"../LazyView\";\nimport { CTX } from \"../store\";\n\nexport type TAnimationStyle = (value: number) => AnimatedStyleProp<ViewStyle>;\n\nexport const BaseLayout: React.FC<{\n index: number\n handlerOffset: Animated.SharedValue<number>\n visibleRanges: IVisibleRanges\n animationStyle: TAnimationStyle\n children: (ctx: {\n animationValue: Animated.SharedValue<number>\n }) => React.ReactElement\n}> = (props) => {\n const mounted = useCheckMounted();\n const { handlerOffset, index, children, visibleRanges, animationStyle }\n = props;\n\n const context = React.useContext(CTX);\n const {\n props: {\n loop,\n dataLength,\n width,\n height,\n vertical,\n customConfig,\n mode,\n modeConfig,\n },\n } = context;\n const size = vertical ? height : width;\n const [shouldUpdate, setShouldUpdate] = React.useState(false);\n let offsetXConfig: IOpts = {\n handlerOffset,\n index,\n size,\n dataLength,\n loop,\n ...(typeof customConfig === \"function\" ? customConfig() : {}),\n };\n\n if (mode === \"horizontal-stack\") {\n const { snapDirection, showLength } = modeConfig as ILayoutConfig;\n\n offsetXConfig = {\n handlerOffset,\n index,\n size,\n dataLength,\n loop,\n type: snapDirection === \"right\" ? \"negative\" : \"positive\",\n viewCount: showLength,\n };\n }\n\n const x = useOffsetX(offsetXConfig, visibleRanges);\n const animationValue = useDerivedValue(() => x.value / size, [x, size]);\n const animatedStyle = useAnimatedStyle(\n () => animationStyle(x.value / size),\n [animationStyle],\n );\n\n const updateView = React.useCallback(\n (negativeRange: number[], positiveRange: number[]) => {\n mounted.current\n && setShouldUpdate(\n (index >= negativeRange[0] && index <= negativeRange[1])\n || (index >= positiveRange[0] && index <= positiveRange[1]),\n );\n },\n [index, mounted],\n );\n\n useAnimatedReaction(\n () => visibleRanges.value,\n () => {\n runOnJS(updateView)(\n visibleRanges.value.negativeRange,\n visibleRanges.value.positiveRange,\n );\n },\n [visibleRanges.value],\n );\n\n return (\n <Animated.View\n style={[\n {\n width: width || \"100%\",\n height: height || \"100%\",\n position: \"absolute\",\n },\n animatedStyle,\n ]}\n /**\n * We use this testID to know when the carousel item is ready to be tested in test.\n * e.g.\n * The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.\n * */\n testID={`__CAROUSEL_ITEM_${index}_${shouldUpdate ? \"READY\" : \"NOT_READY\"}__`}\n >\n <LazyView shouldUpdate={shouldUpdate}>\n {children({ animationValue })}\n </LazyView>\n </Animated.View>\n );\n};\n"]}
@@ -4,14 +4,10 @@ export function dealWithAnimation(withAnimation) {
4
4
 
5
5
  switch (withAnimation.type) {
6
6
  case "spring":
7
- return (value, cb) => {
8
- return withSpring(value, withAnimation.config, isFinished => cb(isFinished));
9
- };
7
+ return (value, cb) => withSpring(value, withAnimation.config, isFinished => cb(isFinished));
10
8
 
11
9
  case "timing":
12
- return (value, cb) => {
13
- return withTiming(value, withAnimation.config, isFinished => cb(isFinished));
14
- };
10
+ return (value, cb) => withTiming(value, withAnimation.config, isFinished => cb(isFinished));
15
11
  }
16
12
  }
17
13
  //# sourceMappingURL=dealWithAnimation.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["dealWithAnimation.ts"],"names":["withSpring","withTiming","dealWithAnimation","withAnimation","type","value","cb","config","isFinished"],"mappings":"AAAA,SAASA,UAAT,EAAqBC,UAArB,QAAuC,yBAAvC;AAIA,OAAO,SAASC,iBAAT,CACLC,aADK,EAEyD;AAC9D;;AACA,UAAQA,aAAa,CAACC,IAAtB;AACE,SAAK,QAAL;AACE,aAAO,CAACC,KAAD,EAAQC,EAAR,KAAe;AACpB,eAAON,UAAU,CAACK,KAAD,EAAQF,aAAa,CAACI,MAAtB,EAA8BC,UAAU,IACvDF,EAAE,CAACE,UAAD,CADa,CAAjB;AAGD,OAJD;;AAKF,SAAK,QAAL;AACE,aAAO,CAACH,KAAD,EAAQC,EAAR,KAAe;AACpB,eAAOL,UAAU,CAACI,KAAD,EAAQF,aAAa,CAACI,MAAtB,EAA8BC,UAAU,IACvDF,EAAE,CAACE,UAAD,CADa,CAAjB;AAGD,OAJD;AARJ;AAcD","sourcesContent":["import { withSpring, withTiming } from \"react-native-reanimated\";\n\nimport type { WithAnimation } from \"../types\";\n\nexport function dealWithAnimation(\n withAnimation: WithAnimation,\n): (value: number, cb: (isFinished: boolean) => void) => number {\n \"worklet\";\n switch (withAnimation.type) {\n case \"spring\":\n return (value, cb) => {\n return withSpring(value, withAnimation.config, isFinished =>\n cb(isFinished as boolean),\n );\n };\n case \"timing\":\n return (value, cb) => {\n return withTiming(value, withAnimation.config, isFinished =>\n cb(isFinished as boolean),\n );\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["dealWithAnimation.ts"],"names":["withSpring","withTiming","dealWithAnimation","withAnimation","type","value","cb","config","isFinished"],"mappings":"AAAA,SAASA,UAAT,EAAqBC,UAArB,QAAuC,yBAAvC;AAIA,OAAO,SAASC,iBAAT,CACLC,aADK,EAEyD;AAC9D;;AACA,UAAQA,aAAa,CAACC,IAAtB;AACE,SAAK,QAAL;AACE,aAAO,CAACC,KAAD,EAAQC,EAAR,KAAeN,UAAU,CAACK,KAAD,EAAQF,aAAa,CAACI,MAAtB,EAA8BC,UAAU,IACtEF,EAAE,CAACE,UAAD,CAD4B,CAAhC;;AAGF,SAAK,QAAL;AACE,aAAO,CAACH,KAAD,EAAQC,EAAR,KAAeL,UAAU,CAACI,KAAD,EAAQF,aAAa,CAACI,MAAtB,EAA8BC,UAAU,IACtEF,EAAE,CAACE,UAAD,CAD4B,CAAhC;AANJ;AAUD","sourcesContent":["import { withSpring, withTiming } from \"react-native-reanimated\";\n\nimport type { WithAnimation } from \"../types\";\n\nexport function dealWithAnimation(\n withAnimation: WithAnimation,\n): (value: number, cb: (isFinished: boolean) => void) => number {\n \"worklet\";\n switch (withAnimation.type) {\n case \"spring\":\n return (value, cb) => withSpring(value, withAnimation.config, isFinished =>\n cb(isFinished as boolean),\n );\n case \"timing\":\n return (value, cb) => withTiming(value, withAnimation.config, isFinished =>\n cb(isFinished as boolean),\n );\n }\n}\n"]}
@@ -115,15 +115,14 @@ export declare type TCarouselProps<T = any> = {
115
115
  /**
116
116
  * If enabled, releasing the touch will scroll to the nearest item.
117
117
  * valid when pagingEnabled=false
118
- * @deprecated please use snapEnabled instead
118
+ * @default true
119
119
  */
120
- enableSnap?: boolean;
120
+ snapEnabled?: boolean;
121
121
  /**
122
- * If enabled, releasing the touch will scroll to the nearest item.
123
- * valid when pagingEnabled=false
122
+ * If enabled, items will scroll to the first placement when scrolling past the edge rather than closing to the last. (previous conditions: loop=false)
124
123
  * @default true
125
124
  */
126
- snapEnabled?: boolean;
125
+ overscrollEnabled?: boolean;
127
126
  /**
128
127
  * If false, Carousel will not respond to any gestures.
129
128
  * @default true
@@ -168,6 +167,12 @@ export declare type TCarouselProps<T = any> = {
168
167
  * @param absoluteProgress Convert to index (0 1 2 ...)
169
168
  */
170
169
  onProgressChange?: (offsetProgress: number, absoluteProgress: number) => void;
170
+ /**
171
+ * If enabled, releasing the touch will scroll to the nearest item.
172
+ * valid when pagingEnabled=false
173
+ * @deprecated please use snapEnabled instead
174
+ */
175
+ enableSnap?: boolean;
171
176
  } & (TParallaxModeProps | TStackModeProps);
172
177
  export interface ICarouselInstance {
173
178
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reanimated-carousel",
3
- "version": "3.1.5",
3
+ "version": "3.3.0",
4
4
  "description": "Simple carousel component.fully implemented using Reanimated 2.Infinitely scrolling, very smooth.",
5
5
  "main": "lib/commonjs/index",
6
6
  "react-native": "src/index.tsx",
@@ -22,11 +22,11 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "gif": "node scripts/makegif.js ./scripts/gif-works-directory",
25
- "test": "vitest run src/**/*",
26
- "test:dev": "vitest dev src/**/*",
25
+ "test": "jest run src/**/*",
26
+ "test:dev": "jest dev src/**/*",
27
27
  "typescript": "tsc --noEmit",
28
- "lint": "eslint \"src/**/*.{js,ts,tsx}\"",
29
- "lint:fix": "eslint \"src/**/*.{js,ts,tsx}\" --fix",
28
+ "lint": "eslint 'src/**/*.{js,ts,tsx}'",
29
+ "lint:fix": "eslint 'src/**/*.{js,ts,tsx}' --fix",
30
30
  "dev": "yarn watch 'yarn prepare' ./src",
31
31
  "prepare": "bob build",
32
32
  "release": "yarn prepare && dotenv release-it --no-git.requireUpstream",
@@ -39,7 +39,9 @@
39
39
  "android:pretty": "yarn --cwd exampleExpo android:pretty",
40
40
  "pods": "cd exampleExpo && pod-install --quiet",
41
41
  "bootstrap": "yarn && yarn pods",
42
- "deploy": "cd exampleExpo && yarn deploy"
42
+ "deploy": "cd exampleExpo && yarn deploy",
43
+ "publish": "yarn run prepare && changeset publish",
44
+ "version": "changeset version"
43
45
  },
44
46
  "keywords": [
45
47
  "react-native",
@@ -58,11 +60,13 @@
58
60
  },
59
61
  "devDependencies": {
60
62
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
63
+ "@changesets/changelog-git": "^0.1.14",
64
+ "@changesets/cli": "^2.26.0",
61
65
  "@commitlint/config-conventional": "^11.0.0",
62
66
  "@dohooo/eslint-config": "^0.0.7",
63
67
  "@react-native-community/eslint-config": "^2.0.0",
64
68
  "@release-it/conventional-changelog": "^2.0.0",
65
- "@types/jest": "^26.0.0",
69
+ "@types/jest": "^29.2.5",
66
70
  "@types/react": "~17.0.2",
67
71
  "@types/react-native": "^0.66.16",
68
72
  "@types/react-native-snap-carousel": "^3.8.5",
@@ -76,7 +80,7 @@
76
80
  "eslint-plugin-prettier": "^3.1.3",
77
81
  "gifify": "^2.4.3",
78
82
  "husky": "^4.2.5",
79
- "jest": "^26.0.1",
83
+ "jest": "^29.3.1",
80
84
  "pod-install": "^0.1.0",
81
85
  "prettier": "^2.0.5",
82
86
  "react": "16.13.1",
@@ -86,8 +90,7 @@
86
90
  "react-native-reanimated": "2.8.0",
87
91
  "release-it": "^14.2.2",
88
92
  "sponsorkit": "^0.1.3",
89
- "typescript": "^4.0.8",
90
- "vitest": "^0.24.3"
93
+ "typescript": "^4.0.8"
91
94
  },
92
95
  "peerDependencies": {
93
96
  "react": ">=16.8.0",
@@ -6,9 +6,11 @@ import {
6
6
  } from "react-native-gesture-handler";
7
7
  import Animated, {
8
8
  cancelAnimation,
9
+ measure,
9
10
  runOnJS,
10
11
  useAnimatedGestureHandler,
11
12
  useAnimatedReaction,
13
+ useAnimatedRef,
12
14
  useDerivedValue,
13
15
  useSharedValue,
14
16
  withDecay,
@@ -49,6 +51,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
49
51
  withAnimation,
50
52
  enabled,
51
53
  dataLength,
54
+ overscrollEnabled,
52
55
  },
53
56
  } = React.useContext(CTX);
54
57
 
@@ -68,8 +71,27 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
68
71
  const touching = useSharedValue(false);
69
72
  const scrollEndTranslation = useSharedValue(0);
70
73
  const scrollEndVelocity = useSharedValue(0);
74
+ const containerRef = useAnimatedRef<Animated.View>();
71
75
 
72
- const _withSpring = React.useCallback(
76
+ // Get the limit of the scroll.
77
+ const getLimit = React.useCallback(() => {
78
+ "worklet";
79
+
80
+ if (!infinite && !overscrollEnabled) {
81
+ const { width: containerWidth = 0 } = measure(containerRef);
82
+
83
+ // If the item's total width is less than the container's width, then there is no need to scroll.
84
+ if (dataLength * size < containerWidth)
85
+ return 0;
86
+
87
+ // Disable the "overscroll" effect
88
+ return dataLength * size - containerWidth;
89
+ }
90
+
91
+ return dataLength * size;
92
+ }, [infinite, size, dataLength, overscrollEnabled]);
93
+
94
+ const withSpring = React.useCallback(
73
95
  (toValue: number, onFinished?: () => void) => {
74
96
  "worklet";
75
97
  const defaultWithAnimation: WithTimingAnimation = {
@@ -97,34 +119,49 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
97
119
  "worklet";
98
120
  const origin = translation.value;
99
121
  const velocity = scrollEndVelocity.value;
100
- if (!pagingEnabled) {
101
- /**
102
- * If enabled, releasing the touch will scroll to the nearest item.
103
- * valid when pagingEnabled=false
104
- */
105
- if (snapEnabled) {
106
- const nextPage
107
- = Math.round((origin + velocity * 0.4) / size) * size;
108
-
109
- translation.value = _withSpring(nextPage, onFinished);
110
- return;
122
+ // Default to scroll in the direction of the slide (with deceleration)
123
+ let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 });
124
+
125
+ /**
126
+ * The page size is the same as the item size.
127
+ * If direction is vertical, the page size is the height of the item.
128
+ * If direction is horizontal, the page size is the width of the item.
129
+ *
130
+ * `page size` equals to `size` variable.
131
+ * */
132
+ if (pagingEnabled) {
133
+ // distance with direction
134
+ const offset = -(scrollEndTranslation.value >= 0 ? 1 : -1); // 1 or -1
135
+ const computed = offset < 0 ? Math.ceil : Math.floor;
136
+ const page = computed(-translation.value / size);
137
+
138
+ if (infinite) {
139
+ const finalPage = page + offset;
140
+ finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished);
141
+ }
142
+ else {
143
+ const finalPage = Math.min(maxPage - 1, Math.max(0, page + offset));
144
+ finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished);
111
145
  }
112
- translation.value = withDecay({
113
- velocity,
114
- deceleration: 0.999,
115
- });
116
- return;
117
146
  }
118
147
 
119
- const direction = -(scrollEndTranslation.value >= 0 ? 1 : -1);
120
- const computed = direction < 0 ? Math.ceil : Math.floor;
121
- const page = computed(-translation.value / size);
122
- let finalPage = page + direction;
148
+ if (!pagingEnabled && snapEnabled) {
149
+ // scroll to the nearest item
150
+ const nextPage = Math.round((origin + velocity * 0.4) / size) * size;
151
+ finalTranslation = withSpring(withProcessTranslation(nextPage), onFinished);
152
+ }
123
153
 
124
- if (!infinite)
125
- finalPage = Math.min(maxPage - 1, Math.max(0, finalPage));
154
+ translation.value = finalTranslation;
155
+
156
+ function withProcessTranslation(translation: number) {
157
+ if (!infinite && !overscrollEnabled) {
158
+ const limit = getLimit();
159
+ const sign = Math.sign(translation);
160
+ return sign * Math.max(0, Math.min(limit, Math.abs(translation)));
161
+ }
126
162
 
127
- translation.value = _withSpring(-finalPage * size, onFinished);
163
+ return translation;
164
+ }
128
165
  },
129
166
  [
130
167
  translation,
@@ -133,7 +170,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
133
170
  size,
134
171
  scrollEndTranslation.value,
135
172
  infinite,
136
- _withSpring,
173
+ withSpring,
137
174
  snapEnabled,
138
175
  maxPage,
139
176
  ],
@@ -170,7 +207,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
170
207
  return;
171
208
  }
172
209
  if (!infinite) {
173
- translation.value = _withSpring(0);
210
+ translation.value = withSpring(0);
174
211
  return;
175
212
  }
176
213
  }
@@ -181,7 +218,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
181
218
  return;
182
219
  }
183
220
  if (!infinite)
184
- translation.value = _withSpring(-((maxPage - 1) * size));
221
+ translation.value = withSpring(-((maxPage - 1) * size));
185
222
  }
186
223
  }, [
187
224
  touching.value,
@@ -191,7 +228,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
191
228
  scrollEndTranslation.value,
192
229
  infinite,
193
230
  activeDecay,
194
- _withSpring,
231
+ withSpring,
195
232
  ]);
196
233
 
197
234
  useAnimatedReaction(
@@ -212,7 +249,11 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
212
249
  touching.value = true;
213
250
  ctx.validStart = true;
214
251
  onScrollBegin && runOnJS(onScrollBegin)();
252
+
215
253
  ctx.max = (maxPage - 1) * size;
254
+ if (!infinite && !overscrollEnabled)
255
+ ctx.max = getLimit();
256
+
216
257
  ctx.panOffset = translation.value;
217
258
  },
218
259
  onActive: (e, ctx) => {
@@ -225,19 +266,18 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
225
266
  const panTranslation = isHorizontal.value
226
267
  ? translationX
227
268
  : translationY;
228
-
229
- if (
230
- !infinite
231
- && (translation.value > 0 || translation.value < -ctx.max)
232
- ) {
233
- const boundary = translation.value > 0 ? 0 : -ctx.max;
234
- const fixed = boundary - ctx.panOffset;
235
- const dynamic = panTranslation - fixed;
236
- translation.value = boundary + dynamic * 0.5;
237
- return;
269
+ if (!infinite) {
270
+ if ((translation.value > 0 || translation.value < -ctx.max)) {
271
+ const boundary = translation.value > 0 ? 0 : -ctx.max;
272
+ const fixed = boundary - ctx.panOffset;
273
+ const dynamic = panTranslation - fixed;
274
+ translation.value = boundary + dynamic * 0.5;
275
+ return;
276
+ }
238
277
  }
239
278
 
240
- translation.value = ctx.panOffset + panTranslation;
279
+ const translationValue = ctx.panOffset + panTranslation;
280
+ translation.value = translationValue;
241
281
  },
242
282
  onEnd: (e) => {
243
283
  const { velocityX, velocityY, translationX, translationY } = e;
@@ -273,6 +313,7 @@ const IScrollViewGesture: React.FC<Props> = (props) => {
273
313
  onGestureEvent={panGestureEventHandler}
274
314
  >
275
315
  <Animated.View
316
+ ref={containerRef}
276
317
  testID={testID}
277
318
  style={style}
278
319
  onTouchStart={onTouchBegin}
@@ -1,5 +1,3 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
1
  import { computeNewIndexWhenDataChanges } from "./computeNewIndexWhenDataChanges";
4
2
 
5
3
  describe("should work as expected", () => {
@@ -31,13 +31,15 @@ export function useInitProps<T>(
31
31
  defaultIndex = 0,
32
32
  data: rawData = [],
33
33
  loop = true,
34
- enabled = true,
35
34
  autoPlayInterval: _autoPlayInterval = 1000,
36
35
  scrollAnimationDuration = 500,
37
36
  style = {},
38
37
  panGestureHandlerProps = {},
39
- pagingEnabled = true,
40
38
  autoFillData = true,
39
+ // switchers
40
+ enabled = true,
41
+ pagingEnabled = true,
42
+ overscrollEnabled = true,
41
43
  snapEnabled = props.enableSnap ?? true,
42
44
  width: _width,
43
45
  height: _height,
@@ -89,6 +91,7 @@ export function useInitProps<T>(
89
91
  panGestureHandlerProps,
90
92
  pagingEnabled,
91
93
  snapEnabled,
94
+ overscrollEnabled,
92
95
  width,
93
96
  height,
94
97
  };
@@ -30,7 +30,7 @@ export const BaseLayout: React.FC<{
30
30
  }> = (props) => {
31
31
  const mounted = useCheckMounted();
32
32
  const { handlerOffset, index, children, visibleRanges, animationStyle }
33
- = props;
33
+ = props;
34
34
 
35
35
  const context = React.useContext(CTX);
36
36
  const {
@@ -109,6 +109,12 @@ export const BaseLayout: React.FC<{
109
109
  },
110
110
  animatedStyle,
111
111
  ]}
112
+ /**
113
+ * We use this testID to know when the carousel item is ready to be tested in test.
114
+ * e.g.
115
+ * The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.
116
+ * */
117
+ testID={`__CAROUSEL_ITEM_${index}_${shouldUpdate ? "READY" : "NOT_READY"}__`}
112
118
  >
113
119
  <LazyView shouldUpdate={shouldUpdate}>
114
120
  {children({ animationValue })}
package/src/types.ts CHANGED
@@ -132,15 +132,14 @@ export type TCarouselProps<T = any> = {
132
132
  /**
133
133
  * If enabled, releasing the touch will scroll to the nearest item.
134
134
  * valid when pagingEnabled=false
135
- * @deprecated please use snapEnabled instead
135
+ * @default true
136
136
  */
137
- enableSnap?: boolean
137
+ snapEnabled?: boolean
138
138
  /**
139
- * If enabled, releasing the touch will scroll to the nearest item.
140
- * valid when pagingEnabled=false
139
+ * If enabled, items will scroll to the first placement when scrolling past the edge rather than closing to the last. (previous conditions: loop=false)
141
140
  * @default true
142
141
  */
143
- snapEnabled?: boolean
142
+ overscrollEnabled?: boolean
144
143
  /**
145
144
  * If false, Carousel will not respond to any gestures.
146
145
  * @default true
@@ -188,6 +187,14 @@ export type TCarouselProps<T = any> = {
188
187
  offsetProgress: number,
189
188
  absoluteProgress: number
190
189
  ) => void
190
+
191
+ // ============================== deprecated props ==============================
192
+ /**
193
+ * If enabled, releasing the touch will scroll to the nearest item.
194
+ * valid when pagingEnabled=false
195
+ * @deprecated please use snapEnabled instead
196
+ */
197
+ enableSnap?: boolean
191
198
  } & (TParallaxModeProps | TStackModeProps);
192
199
 
193
200
  export interface ICarouselInstance {
@@ -8,16 +8,12 @@ export function dealWithAnimation(
8
8
  "worklet";
9
9
  switch (withAnimation.type) {
10
10
  case "spring":
11
- return (value, cb) => {
12
- return withSpring(value, withAnimation.config, isFinished =>
13
- cb(isFinished as boolean),
14
- );
15
- };
11
+ return (value, cb) => withSpring(value, withAnimation.config, isFinished =>
12
+ cb(isFinished as boolean),
13
+ );
16
14
  case "timing":
17
- return (value, cb) => {
18
- return withTiming(value, withAnimation.config, isFinished =>
19
- cb(isFinished as boolean),
20
- );
21
- };
15
+ return (value, cb) => withTiming(value, withAnimation.config, isFinished =>
16
+ cb(isFinished as boolean),
17
+ );
22
18
  }
23
19
  }