@leonsilicon/react-native-reanimated-carousel 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +652 -0
- package/lib/commonjs/components/Carousel.js +2 -0
- package/lib/commonjs/components/Carousel.js.map +1 -0
- package/lib/commonjs/components/CarouselLayout.js +2 -0
- package/lib/commonjs/components/CarouselLayout.js.map +1 -0
- package/lib/commonjs/components/ItemLayout.js +2 -0
- package/lib/commonjs/components/ItemLayout.js.map +1 -0
- package/lib/commonjs/components/ItemRenderer.js +2 -0
- package/lib/commonjs/components/ItemRenderer.js.map +1 -0
- package/lib/commonjs/components/LazyView.js +2 -0
- package/lib/commonjs/components/LazyView.js.map +1 -0
- package/lib/commonjs/components/Pagination/Basic/PaginationItem.js +2 -0
- package/lib/commonjs/components/Pagination/Basic/PaginationItem.js.map +1 -0
- package/lib/commonjs/components/Pagination/Basic/index.js +2 -0
- package/lib/commonjs/components/Pagination/Basic/index.js.map +1 -0
- package/lib/commonjs/components/Pagination/Custom/PaginationItem.js +2 -0
- package/lib/commonjs/components/Pagination/Custom/PaginationItem.js.map +1 -0
- package/lib/commonjs/components/Pagination/Custom/index.js +2 -0
- package/lib/commonjs/components/Pagination/Custom/index.js.map +1 -0
- package/lib/commonjs/components/Pagination/index.js +2 -0
- package/lib/commonjs/components/Pagination/index.js.map +1 -0
- package/lib/commonjs/components/ScrollViewGesture.js +2 -0
- package/lib/commonjs/components/ScrollViewGesture.js.map +1 -0
- package/lib/commonjs/constants/index.js +2 -0
- package/lib/commonjs/constants/index.js.map +1 -0
- package/lib/commonjs/hooks/useAutoPlay.js +2 -0
- package/lib/commonjs/hooks/useAutoPlay.js.map +1 -0
- package/lib/commonjs/hooks/useCarouselController.js +2 -0
- package/lib/commonjs/hooks/useCarouselController.js.map +1 -0
- package/lib/commonjs/hooks/useCheckMounted.js +2 -0
- package/lib/commonjs/hooks/useCheckMounted.js.map +1 -0
- package/lib/commonjs/hooks/useCommonVariables.js +2 -0
- package/lib/commonjs/hooks/useCommonVariables.js.map +1 -0
- package/lib/commonjs/hooks/useInitProps.js +2 -0
- package/lib/commonjs/hooks/useInitProps.js.map +1 -0
- package/lib/commonjs/hooks/useLayoutConfig.js +2 -0
- package/lib/commonjs/hooks/useLayoutConfig.js.map +1 -0
- package/lib/commonjs/hooks/useOffsetX.js +2 -0
- package/lib/commonjs/hooks/useOffsetX.js.map +1 -0
- package/lib/commonjs/hooks/useOnProgressChange.js +2 -0
- package/lib/commonjs/hooks/useOnProgressChange.js.map +1 -0
- package/lib/commonjs/hooks/usePanGestureProxy.js +2 -0
- package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -0
- package/lib/commonjs/hooks/usePropsErrorBoundary.js +2 -0
- package/lib/commonjs/hooks/usePropsErrorBoundary.js.map +1 -0
- package/lib/commonjs/hooks/useSizeResolver.js +2 -0
- package/lib/commonjs/hooks/useSizeResolver.js.map +1 -0
- package/lib/commonjs/hooks/useUpdateGestureConfig.js +2 -0
- package/lib/commonjs/hooks/useUpdateGestureConfig.js.map +1 -0
- package/lib/commonjs/hooks/useVisibleRanges.js +2 -0
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -0
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/layouts/index.js +2 -0
- package/lib/commonjs/layouts/index.js.map +1 -0
- package/lib/commonjs/layouts/normal.js +2 -0
- package/lib/commonjs/layouts/normal.js.map +1 -0
- package/lib/commonjs/layouts/parallax.js +2 -0
- package/lib/commonjs/layouts/parallax.js.map +1 -0
- package/lib/commonjs/layouts/stack.js +2 -0
- package/lib/commonjs/layouts/stack.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/store/index.js +2 -0
- package/lib/commonjs/store/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/utils/compute-gesture-translation.js +2 -0
- package/lib/commonjs/utils/compute-gesture-translation.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-data-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-data-changed.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-size-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-size-changed.js.map +1 -0
- package/lib/commonjs/utils/compute-offset-if-sizes-changed.js +2 -0
- package/lib/commonjs/utils/compute-offset-if-sizes-changed.js.map +1 -0
- package/lib/commonjs/utils/computed-with-auto-fill-data.js +2 -0
- package/lib/commonjs/utils/computed-with-auto-fill-data.js.map +1 -0
- package/lib/commonjs/utils/deal-with-animation.js +2 -0
- package/lib/commonjs/utils/deal-with-animation.js.map +1 -0
- package/lib/commonjs/utils/handleroffset-direction.js +2 -0
- package/lib/commonjs/utils/handleroffset-direction.js.map +1 -0
- package/lib/commonjs/utils/log.js +2 -0
- package/lib/commonjs/utils/log.js.map +1 -0
- package/lib/commonjs/utils/sanitize-animation-style.js +2 -0
- package/lib/commonjs/utils/sanitize-animation-style.js.map +1 -0
- package/lib/commonjs/utils/size-resolver.js +2 -0
- package/lib/commonjs/utils/size-resolver.js.map +1 -0
- package/lib/module/components/Carousel.js +2 -0
- package/lib/module/components/Carousel.js.map +1 -0
- package/lib/module/components/CarouselLayout.js +2 -0
- package/lib/module/components/CarouselLayout.js.map +1 -0
- package/lib/module/components/ItemLayout.js +2 -0
- package/lib/module/components/ItemLayout.js.map +1 -0
- package/lib/module/components/ItemRenderer.js +2 -0
- package/lib/module/components/ItemRenderer.js.map +1 -0
- package/lib/module/components/LazyView.js +2 -0
- package/lib/module/components/LazyView.js.map +1 -0
- package/lib/module/components/Pagination/Basic/PaginationItem.js +2 -0
- package/lib/module/components/Pagination/Basic/PaginationItem.js.map +1 -0
- package/lib/module/components/Pagination/Basic/index.js +2 -0
- package/lib/module/components/Pagination/Basic/index.js.map +1 -0
- package/lib/module/components/Pagination/Custom/PaginationItem.js +2 -0
- package/lib/module/components/Pagination/Custom/PaginationItem.js.map +1 -0
- package/lib/module/components/Pagination/Custom/index.js +2 -0
- package/lib/module/components/Pagination/Custom/index.js.map +1 -0
- package/lib/module/components/Pagination/index.js +2 -0
- package/lib/module/components/Pagination/index.js.map +1 -0
- package/lib/module/components/ScrollViewGesture.js +2 -0
- package/lib/module/components/ScrollViewGesture.js.map +1 -0
- package/lib/module/constants/index.js +2 -0
- package/lib/module/constants/index.js.map +1 -0
- package/lib/module/hooks/useAutoPlay.js +2 -0
- package/lib/module/hooks/useAutoPlay.js.map +1 -0
- package/lib/module/hooks/useCarouselController.js +2 -0
- package/lib/module/hooks/useCarouselController.js.map +1 -0
- package/lib/module/hooks/useCheckMounted.js +2 -0
- package/lib/module/hooks/useCheckMounted.js.map +1 -0
- package/lib/module/hooks/useCommonVariables.js +2 -0
- package/lib/module/hooks/useCommonVariables.js.map +1 -0
- package/lib/module/hooks/useInitProps.js +2 -0
- package/lib/module/hooks/useInitProps.js.map +1 -0
- package/lib/module/hooks/useLayoutConfig.js +2 -0
- package/lib/module/hooks/useLayoutConfig.js.map +1 -0
- package/lib/module/hooks/useOffsetX.js +2 -0
- package/lib/module/hooks/useOffsetX.js.map +1 -0
- package/lib/module/hooks/useOnProgressChange.js +2 -0
- package/lib/module/hooks/useOnProgressChange.js.map +1 -0
- package/lib/module/hooks/usePanGestureProxy.js +2 -0
- package/lib/module/hooks/usePanGestureProxy.js.map +1 -0
- package/lib/module/hooks/usePropsErrorBoundary.js +2 -0
- package/lib/module/hooks/usePropsErrorBoundary.js.map +1 -0
- package/lib/module/hooks/useSizeResolver.js +2 -0
- package/lib/module/hooks/useSizeResolver.js.map +1 -0
- package/lib/module/hooks/useUpdateGestureConfig.js +2 -0
- package/lib/module/hooks/useUpdateGestureConfig.js.map +1 -0
- package/lib/module/hooks/useVisibleRanges.js +2 -0
- package/lib/module/hooks/useVisibleRanges.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/layouts/index.js +2 -0
- package/lib/module/layouts/index.js.map +1 -0
- package/lib/module/layouts/normal.js +2 -0
- package/lib/module/layouts/normal.js.map +1 -0
- package/lib/module/layouts/parallax.js +2 -0
- package/lib/module/layouts/parallax.js.map +1 -0
- package/lib/module/layouts/stack.js +2 -0
- package/lib/module/layouts/stack.js.map +1 -0
- package/lib/module/store/index.js +2 -0
- package/lib/module/store/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/compute-gesture-translation.js +2 -0
- package/lib/module/utils/compute-gesture-translation.js.map +1 -0
- package/lib/module/utils/compute-offset-if-data-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-data-changed.js.map +1 -0
- package/lib/module/utils/compute-offset-if-size-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-size-changed.js.map +1 -0
- package/lib/module/utils/compute-offset-if-sizes-changed.js +2 -0
- package/lib/module/utils/compute-offset-if-sizes-changed.js.map +1 -0
- package/lib/module/utils/computed-with-auto-fill-data.js +2 -0
- package/lib/module/utils/computed-with-auto-fill-data.js.map +1 -0
- package/lib/module/utils/deal-with-animation.js +2 -0
- package/lib/module/utils/deal-with-animation.js.map +1 -0
- package/lib/module/utils/handleroffset-direction.js +2 -0
- package/lib/module/utils/handleroffset-direction.js.map +1 -0
- package/lib/module/utils/log.js +2 -0
- package/lib/module/utils/log.js.map +1 -0
- package/lib/module/utils/sanitize-animation-style.js +2 -0
- package/lib/module/utils/sanitize-animation-style.js.map +1 -0
- package/lib/module/utils/size-resolver.js +2 -0
- package/lib/module/utils/size-resolver.js.map +1 -0
- package/lib/typescript/components/Carousel.d.ts +8 -0
- package/lib/typescript/components/Carousel.d.ts.map +1 -0
- package/lib/typescript/components/CarouselLayout.d.ts +6 -0
- package/lib/typescript/components/CarouselLayout.d.ts.map +1 -0
- package/lib/typescript/components/ItemLayout.d.ts +15 -0
- package/lib/typescript/components/ItemLayout.d.ts.map +1 -0
- package/lib/typescript/components/ItemRenderer.d.ts +24 -0
- package/lib/typescript/components/ItemRenderer.d.ts.map +1 -0
- package/lib/typescript/components/LazyView.d.ts +8 -0
- package/lib/typescript/components/LazyView.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts +29 -0
- package/lib/typescript/components/Pagination/Basic/PaginationItem.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Basic/index.d.ts +23 -0
- package/lib/typescript/components/Pagination/Basic/index.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts +35 -0
- package/lib/typescript/components/Pagination/Custom/PaginationItem.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/Custom/index.d.ts +26 -0
- package/lib/typescript/components/Pagination/Custom/index.d.ts.map +1 -0
- package/lib/typescript/components/Pagination/index.d.ts +5 -0
- package/lib/typescript/components/Pagination/index.d.ts.map +1 -0
- package/lib/typescript/components/ScrollViewGesture.d.ts +19 -0
- package/lib/typescript/components/ScrollViewGesture.d.ts.map +1 -0
- package/lib/typescript/constants/index.d.ts +9 -0
- package/lib/typescript/constants/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useAutoPlay.d.ts +12 -0
- package/lib/typescript/hooks/useAutoPlay.d.ts.map +1 -0
- package/lib/typescript/hooks/useCarouselController.d.ts +28 -0
- package/lib/typescript/hooks/useCarouselController.d.ts.map +1 -0
- package/lib/typescript/hooks/useCheckMounted.d.ts +3 -0
- package/lib/typescript/hooks/useCheckMounted.d.ts.map +1 -0
- package/lib/typescript/hooks/useCommonVariables.d.ts +13 -0
- package/lib/typescript/hooks/useCommonVariables.d.ts.map +1 -0
- package/lib/typescript/hooks/useInitProps.d.ts +17 -0
- package/lib/typescript/hooks/useInitProps.d.ts.map +1 -0
- package/lib/typescript/hooks/useLayoutConfig.d.ts +10 -0
- package/lib/typescript/hooks/useLayoutConfig.d.ts.map +1 -0
- package/lib/typescript/hooks/useOffsetX.d.ts +24 -0
- package/lib/typescript/hooks/useOffsetX.d.ts.map +1 -0
- package/lib/typescript/hooks/useOnProgressChange.d.ts +14 -0
- package/lib/typescript/hooks/useOnProgressChange.d.ts.map +1 -0
- package/lib/typescript/hooks/usePanGestureProxy.d.ts +10 -0
- package/lib/typescript/hooks/usePanGestureProxy.d.ts.map +1 -0
- package/lib/typescript/hooks/usePropsErrorBoundary.d.ts +5 -0
- package/lib/typescript/hooks/usePropsErrorBoundary.d.ts.map +1 -0
- package/lib/typescript/hooks/useSizeResolver.d.ts +27 -0
- package/lib/typescript/hooks/useSizeResolver.d.ts.map +1 -0
- package/lib/typescript/hooks/useUpdateGestureConfig.d.ts +6 -0
- package/lib/typescript/hooks/useUpdateGestureConfig.d.ts.map +1 -0
- package/lib/typescript/hooks/useVisibleRanges.d.ts +23 -0
- package/lib/typescript/hooks/useVisibleRanges.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +7 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/layouts/index.d.ts +11 -0
- package/lib/typescript/layouts/index.d.ts.map +1 -0
- package/lib/typescript/layouts/normal.d.ts +16 -0
- package/lib/typescript/layouts/normal.d.ts.map +1 -0
- package/lib/typescript/layouts/parallax.d.ts +50 -0
- package/lib/typescript/layouts/parallax.d.ts.map +1 -0
- package/lib/typescript/layouts/stack.d.ts +38 -0
- package/lib/typescript/layouts/stack.d.ts.map +1 -0
- package/lib/typescript/store/index.d.ts +38 -0
- package/lib/typescript/store/index.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +326 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/utils/compute-gesture-translation.d.ts +9 -0
- package/lib/typescript/utils/compute-gesture-translation.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-data-changed.d.ts +9 -0
- package/lib/typescript/utils/compute-offset-if-data-changed.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-size-changed.d.ts +6 -0
- package/lib/typescript/utils/compute-offset-if-size-changed.d.ts.map +1 -0
- package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts +14 -0
- package/lib/typescript/utils/compute-offset-if-sizes-changed.d.ts.map +1 -0
- package/lib/typescript/utils/computed-with-auto-fill-data.d.ts +23 -0
- package/lib/typescript/utils/computed-with-auto-fill-data.d.ts.map +1 -0
- package/lib/typescript/utils/deal-with-animation.d.ts +3 -0
- package/lib/typescript/utils/deal-with-animation.d.ts.map +1 -0
- package/lib/typescript/utils/handleroffset-direction.d.ts +4 -0
- package/lib/typescript/utils/handleroffset-direction.d.ts.map +1 -0
- package/lib/typescript/utils/log.d.ts +7 -0
- package/lib/typescript/utils/log.d.ts.map +1 -0
- package/lib/typescript/utils/sanitize-animation-style.d.ts +3 -0
- package/lib/typescript/utils/sanitize-animation-style.d.ts.map +1 -0
- package/lib/typescript/utils/size-resolver.d.ts +87 -0
- package/lib/typescript/utils/size-resolver.d.ts.map +1 -0
- package/package.json +151 -0
- package/src/components/Carousel.test.tsx +1153 -0
- package/src/components/Carousel.tsx +35 -0
- package/src/components/CarouselLayout.tsx +231 -0
- package/src/components/ItemLayout.tsx +217 -0
- package/src/components/ItemRenderer.tsx +114 -0
- package/src/components/LazyView.test.tsx +61 -0
- package/src/components/LazyView.tsx +14 -0
- package/src/components/Pagination/Basic/PaginationItem.tsx +149 -0
- package/src/components/Pagination/Basic/index.tsx +98 -0
- package/src/components/Pagination/Custom/PaginationItem.tsx +166 -0
- package/src/components/Pagination/Custom/index.tsx +111 -0
- package/src/components/Pagination/Pagination.test.tsx +178 -0
- package/src/components/Pagination/index.tsx +7 -0
- package/src/components/ScrollViewGesture.tsx +577 -0
- package/src/components/rnr-demo.test.tsx +53 -0
- package/src/constants/index.ts +11 -0
- package/src/hooks/useAutoPlay.test.ts +194 -0
- package/src/hooks/useAutoPlay.ts +58 -0
- package/src/hooks/useCarouselController.test.tsx +1158 -0
- package/src/hooks/useCarouselController.tsx +525 -0
- package/src/hooks/useCheckMounted.test.ts +47 -0
- package/src/hooks/useCheckMounted.ts +14 -0
- package/src/hooks/useCommonVariables.test.tsx +384 -0
- package/src/hooks/useCommonVariables.ts +202 -0
- package/src/hooks/useInitProps.test.tsx +134 -0
- package/src/hooks/useInitProps.ts +111 -0
- package/src/hooks/useLayoutConfig.test.tsx +247 -0
- package/src/hooks/useLayoutConfig.ts +30 -0
- package/src/hooks/useOffsetX.test.ts +110 -0
- package/src/hooks/useOffsetX.ts +109 -0
- package/src/hooks/useOnProgressChange.test.tsx +207 -0
- package/src/hooks/useOnProgressChange.ts +105 -0
- package/src/hooks/usePanGestureProxy.test.tsx +368 -0
- package/src/hooks/usePanGestureProxy.ts +144 -0
- package/src/hooks/usePropsErrorBoundary.ts +138 -0
- package/src/hooks/useSizeResolver.test.tsx +112 -0
- package/src/hooks/useSizeResolver.ts +106 -0
- package/src/hooks/useUpdateGestureConfig.test.ts +89 -0
- package/src/hooks/useUpdateGestureConfig.ts +14 -0
- package/src/hooks/useVisibleRanges.test.tsx +366 -0
- package/src/hooks/useVisibleRanges.tsx +123 -0
- package/src/index.tsx +13 -0
- package/src/layouts/index.tsx +12 -0
- package/src/layouts/normal.ts +32 -0
- package/src/layouts/parallax.test.ts +239 -0
- package/src/layouts/parallax.ts +83 -0
- package/src/layouts/stack.test.ts +252 -0
- package/src/layouts/stack.ts +306 -0
- package/src/store/index.test.tsx +314 -0
- package/src/store/index.tsx +66 -0
- package/src/types.ts +348 -0
- package/src/utils/compute-gesture-translation.test.ts +70 -0
- package/src/utils/compute-gesture-translation.ts +29 -0
- package/src/utils/compute-offset-if-data-changed.test.ts +133 -0
- package/src/utils/compute-offset-if-data-changed.ts +44 -0
- package/src/utils/compute-offset-if-size-changed.test.ts +78 -0
- package/src/utils/compute-offset-if-size-changed.ts +14 -0
- package/src/utils/compute-offset-if-sizes-changed.test.ts +74 -0
- package/src/utils/compute-offset-if-sizes-changed.ts +44 -0
- package/src/utils/computed-with-auto-fill-data.test.ts +298 -0
- package/src/utils/computed-with-auto-fill-data.ts +92 -0
- package/src/utils/deal-with-animation.test.ts +181 -0
- package/src/utils/deal-with-animation.ts +17 -0
- package/src/utils/handleroffset-direction.test.ts +124 -0
- package/src/utils/handleroffset-direction.ts +18 -0
- package/src/utils/index.test.ts +90 -0
- package/src/utils/log.test.ts +134 -0
- package/src/utils/log.ts +12 -0
- package/src/utils/sanitize-animation-style.test.ts +40 -0
- package/src/utils/sanitize-animation-style.ts +20 -0
- package/src/utils/size-resolver.test.ts +193 -0
- package/src/utils/size-resolver.ts +216 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import type { PropsWithChildren } from "react";
|
|
2
|
+
import React, { useCallback } from "react";
|
|
3
|
+
import type { LayoutChangeEvent, StyleProp, ViewStyle } from "react-native";
|
|
4
|
+
import type {
|
|
5
|
+
GestureStateChangeEvent,
|
|
6
|
+
PanGestureHandlerEventPayload,
|
|
7
|
+
} from "react-native-gesture-handler";
|
|
8
|
+
import { GestureDetector } from "react-native-gesture-handler";
|
|
9
|
+
import Animated, {
|
|
10
|
+
cancelAnimation,
|
|
11
|
+
measure,
|
|
12
|
+
useAnimatedReaction,
|
|
13
|
+
useAnimatedRef,
|
|
14
|
+
useDerivedValue,
|
|
15
|
+
useSharedValue,
|
|
16
|
+
withDecay,
|
|
17
|
+
} from "react-native-reanimated";
|
|
18
|
+
import type { SharedValue } from "react-native-reanimated";
|
|
19
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
20
|
+
|
|
21
|
+
import { Easing } from "../constants";
|
|
22
|
+
import { usePanGestureProxy } from "../hooks/usePanGestureProxy";
|
|
23
|
+
import { useGlobalState } from "../store";
|
|
24
|
+
import type { WithTimingAnimation } from "../types";
|
|
25
|
+
import { computeGestureTranslation } from "../utils/compute-gesture-translation";
|
|
26
|
+
import { dealWithAnimation } from "../utils/deal-with-animation";
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
size: number;
|
|
30
|
+
infinite?: boolean;
|
|
31
|
+
testID?: string;
|
|
32
|
+
style?: StyleProp<ViewStyle>;
|
|
33
|
+
translation: SharedValue<number>;
|
|
34
|
+
onLayout?: (e: LayoutChangeEvent) => void;
|
|
35
|
+
onScrollStart?: () => void;
|
|
36
|
+
onScrollEnd?: () => void;
|
|
37
|
+
onTouchBegin?: () => void;
|
|
38
|
+
onTouchEnd?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
|
|
42
|
+
const {
|
|
43
|
+
props: {
|
|
44
|
+
onConfigurePanGesture,
|
|
45
|
+
vertical,
|
|
46
|
+
pagingEnabled,
|
|
47
|
+
snapEnabled,
|
|
48
|
+
loop,
|
|
49
|
+
scrollAnimationDuration,
|
|
50
|
+
withAnimation,
|
|
51
|
+
enabled,
|
|
52
|
+
dataLength,
|
|
53
|
+
overscrollEnabled,
|
|
54
|
+
maxScrollDistancePerSwipe,
|
|
55
|
+
minScrollDistancePerSwipe,
|
|
56
|
+
fixedDirection,
|
|
57
|
+
variableSize,
|
|
58
|
+
},
|
|
59
|
+
common: { size, resolvedSize, sizePhase, sizeExplicit },
|
|
60
|
+
sizeResolver,
|
|
61
|
+
layout: { updateContainerSize },
|
|
62
|
+
} = useGlobalState();
|
|
63
|
+
|
|
64
|
+
const {
|
|
65
|
+
translation,
|
|
66
|
+
testID,
|
|
67
|
+
style = {},
|
|
68
|
+
onScrollStart,
|
|
69
|
+
onScrollEnd,
|
|
70
|
+
onTouchBegin,
|
|
71
|
+
onTouchEnd,
|
|
72
|
+
} = props;
|
|
73
|
+
|
|
74
|
+
const maxPage = dataLength;
|
|
75
|
+
const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
|
|
76
|
+
const max = useSharedValue(0);
|
|
77
|
+
const panOffset = useSharedValue<number | undefined>(undefined); // set to undefined when not actively in a pan gesture
|
|
78
|
+
const touching = useSharedValue(false);
|
|
79
|
+
const validStart = useSharedValue(false);
|
|
80
|
+
const scrollEndTranslation = useSharedValue(0);
|
|
81
|
+
const scrollEndVelocity = useSharedValue(0);
|
|
82
|
+
const containerRef = useAnimatedRef<Animated.View>();
|
|
83
|
+
const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number";
|
|
84
|
+
const minScrollDistancePerSwipeIsSet = typeof minScrollDistancePerSwipe === "number";
|
|
85
|
+
const sizeReady = useDerivedValue(() => {
|
|
86
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
87
|
+
return sizePhase.value === "ready" && currentSize > 0;
|
|
88
|
+
}, [resolvedSize, sizePhase]);
|
|
89
|
+
|
|
90
|
+
// Get the limit of the scroll.
|
|
91
|
+
const getLimit = React.useCallback(() => {
|
|
92
|
+
"worklet";
|
|
93
|
+
|
|
94
|
+
const totalSpan = variableSize ? sizeResolver.total.value : dataLength * size;
|
|
95
|
+
if (totalSpan <= 0) return 0;
|
|
96
|
+
|
|
97
|
+
if (!loop && !overscrollEnabled) {
|
|
98
|
+
const measurement = measure(containerRef);
|
|
99
|
+
const containerWidth = (vertical ? measurement?.height : measurement?.width) || 0;
|
|
100
|
+
|
|
101
|
+
// If the total span is less than the container's width, then there is no
|
|
102
|
+
// need to scroll.
|
|
103
|
+
if (totalSpan < containerWidth) return 0;
|
|
104
|
+
|
|
105
|
+
// Disable the "overscroll" effect
|
|
106
|
+
return totalSpan - containerWidth;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return totalSpan;
|
|
110
|
+
}, [loop, size, dataLength, overscrollEnabled, variableSize, sizeResolver, vertical]);
|
|
111
|
+
|
|
112
|
+
const withSpring = React.useCallback(
|
|
113
|
+
(toValue: number, onFinished?: () => void) => {
|
|
114
|
+
"worklet";
|
|
115
|
+
const defaultWithAnimation: WithTimingAnimation = {
|
|
116
|
+
type: "timing",
|
|
117
|
+
config: {
|
|
118
|
+
duration: scrollAnimationDuration + 100,
|
|
119
|
+
easing: Easing.easeOutQuart,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return dealWithAnimation(withAnimation ?? defaultWithAnimation)(
|
|
124
|
+
toValue,
|
|
125
|
+
(isFinished: boolean) => {
|
|
126
|
+
"worklet";
|
|
127
|
+
if (isFinished) onFinished && scheduleOnRN(onFinished);
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
[scrollAnimationDuration, withAnimation]
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const endWithSpring = React.useCallback(
|
|
135
|
+
(
|
|
136
|
+
scrollEndTranslationValue: number,
|
|
137
|
+
scrollEndVelocityValue: number,
|
|
138
|
+
onFinished?: () => void
|
|
139
|
+
) => {
|
|
140
|
+
"worklet";
|
|
141
|
+
// Use resolvedSize.value (SharedValue) instead of size (React state)
|
|
142
|
+
// to avoid race condition where sizeReady is true but size is still 0
|
|
143
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
144
|
+
const totalSpan = variableSize ? sizeResolver.total.value : 0;
|
|
145
|
+
if (variableSize ? totalSpan <= 0 : currentSize <= 0) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const origin = translation.value;
|
|
149
|
+
const velocity = scrollEndVelocityValue;
|
|
150
|
+
// Default to scroll in the direction of the slide (with deceleration)
|
|
151
|
+
let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 });
|
|
152
|
+
|
|
153
|
+
// Worklet-safe helpers that translate between scroll offsets and item
|
|
154
|
+
// indices. In uniform mode they reduce to the original `offset / size`
|
|
155
|
+
// math; in variable mode they consult the prefix-sum table.
|
|
156
|
+
const indexForOffset = (offset: number): number => {
|
|
157
|
+
if (variableSize) return sizeResolver.indexAt(offset);
|
|
158
|
+
return Math.round(offset / currentSize);
|
|
159
|
+
};
|
|
160
|
+
const offsetForIndex = (index: number): number => {
|
|
161
|
+
if (variableSize) return sizeResolver.offsetAt(index);
|
|
162
|
+
return index * currentSize;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// If the distance of the swipe exceeds the max scroll distance, keep the view at the current position
|
|
166
|
+
if (
|
|
167
|
+
maxScrollDistancePerSwipeIsSet &&
|
|
168
|
+
Math.abs(scrollEndTranslationValue) > maxScrollDistancePerSwipe
|
|
169
|
+
) {
|
|
170
|
+
finalTranslation = origin;
|
|
171
|
+
} else {
|
|
172
|
+
// The final pan position plus a small velocity contribution determines
|
|
173
|
+
// which item we'd naturally land on. `nextPage` is that item's index.
|
|
174
|
+
const nextPage = indexForOffset(-(origin + velocity * 2));
|
|
175
|
+
|
|
176
|
+
if (pagingEnabled) {
|
|
177
|
+
// we'll never go further than a single page away from the current page when paging
|
|
178
|
+
// is enabled.
|
|
179
|
+
|
|
180
|
+
// distance with direction
|
|
181
|
+
const offset = -(scrollEndTranslationValue >= 0 ? 1 : -1); // 1 or -1
|
|
182
|
+
const page = variableSize
|
|
183
|
+
? sizeResolver.indexAt(-origin)
|
|
184
|
+
: (offset < 0 ? Math.ceil : Math.floor)(-origin / currentSize);
|
|
185
|
+
|
|
186
|
+
const velocityDirection = -Math.sign(velocity);
|
|
187
|
+
if (page === nextPage || velocityDirection !== offset) {
|
|
188
|
+
// not going anywhere! Velocity was insufficient to overcome the distance to get to a
|
|
189
|
+
// further page. Let's reset gently to the current page.
|
|
190
|
+
finalTranslation = withSpring(
|
|
191
|
+
withProcessTranslation(-offsetForIndex(page)),
|
|
192
|
+
onFinished
|
|
193
|
+
);
|
|
194
|
+
} else if (loop) {
|
|
195
|
+
const finalPage = page + offset;
|
|
196
|
+
finalTranslation = withSpring(
|
|
197
|
+
withProcessTranslation(-offsetForIndex(finalPage)),
|
|
198
|
+
onFinished
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
const finalPage = Math.min(maxPage - 1, Math.max(0, page + offset));
|
|
202
|
+
finalTranslation = withSpring(
|
|
203
|
+
withProcessTranslation(-offsetForIndex(finalPage)),
|
|
204
|
+
onFinished
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!pagingEnabled && snapEnabled) {
|
|
210
|
+
// scroll to the nearest item
|
|
211
|
+
finalTranslation = withSpring(
|
|
212
|
+
withProcessTranslation(-offsetForIndex(nextPage)),
|
|
213
|
+
onFinished
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
translation.value = finalTranslation;
|
|
219
|
+
|
|
220
|
+
function withProcessTranslation(translation: number) {
|
|
221
|
+
if (!loop && !overscrollEnabled) {
|
|
222
|
+
const limit = getLimit();
|
|
223
|
+
return Math.min(0, Math.max(-limit, translation));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return translation;
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
[
|
|
230
|
+
withSpring,
|
|
231
|
+
resolvedSize,
|
|
232
|
+
maxPage,
|
|
233
|
+
loop,
|
|
234
|
+
snapEnabled,
|
|
235
|
+
translation,
|
|
236
|
+
pagingEnabled,
|
|
237
|
+
maxScrollDistancePerSwipe,
|
|
238
|
+
maxScrollDistancePerSwipeIsSet,
|
|
239
|
+
variableSize,
|
|
240
|
+
sizeResolver,
|
|
241
|
+
overscrollEnabled,
|
|
242
|
+
getLimit,
|
|
243
|
+
]
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const onFinish = React.useCallback(
|
|
247
|
+
(isFinished: boolean) => {
|
|
248
|
+
"worklet";
|
|
249
|
+
if (isFinished) {
|
|
250
|
+
touching.value = false;
|
|
251
|
+
onScrollEnd && scheduleOnRN(onScrollEnd);
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
[onScrollEnd, touching]
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const activeDecay = React.useCallback(() => {
|
|
258
|
+
"worklet";
|
|
259
|
+
touching.value = true;
|
|
260
|
+
translation.value = withDecay({ velocity: scrollEndVelocity.value }, (isFinished) =>
|
|
261
|
+
onFinish(isFinished as boolean)
|
|
262
|
+
);
|
|
263
|
+
}, [onFinish, scrollEndVelocity, touching, translation]);
|
|
264
|
+
|
|
265
|
+
const resetBoundary = React.useCallback(() => {
|
|
266
|
+
"worklet";
|
|
267
|
+
// Use resolvedSize.value (SharedValue) instead of size (React state)
|
|
268
|
+
// to avoid race condition where sizeReady is true but size is still 0
|
|
269
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
270
|
+
const totalSpan = variableSize ? sizeResolver.total.value : 0;
|
|
271
|
+
if (variableSize ? totalSpan <= 0 : currentSize <= 0) return;
|
|
272
|
+
if (touching.value) return;
|
|
273
|
+
|
|
274
|
+
// Maximum negative translation: the start offset of the last item
|
|
275
|
+
// (so the last item sits at the left edge of the viewport).
|
|
276
|
+
const lastItemStart = variableSize
|
|
277
|
+
? sizeResolver.offsetAt(maxPage - 1)
|
|
278
|
+
: (maxPage - 1) * currentSize;
|
|
279
|
+
|
|
280
|
+
if (translation.value > 0) {
|
|
281
|
+
if (scrollEndTranslation.value < 0) {
|
|
282
|
+
activeDecay();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!loop) {
|
|
286
|
+
translation.value = withSpring(0);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (translation.value < -lastItemStart) {
|
|
292
|
+
if (scrollEndTranslation.value > 0) {
|
|
293
|
+
activeDecay();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (!loop) translation.value = withSpring(-lastItemStart);
|
|
297
|
+
}
|
|
298
|
+
}, [
|
|
299
|
+
touching,
|
|
300
|
+
translation,
|
|
301
|
+
maxPage,
|
|
302
|
+
resolvedSize,
|
|
303
|
+
scrollEndTranslation,
|
|
304
|
+
loop,
|
|
305
|
+
activeDecay,
|
|
306
|
+
withSpring,
|
|
307
|
+
variableSize,
|
|
308
|
+
sizeResolver,
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
useAnimatedReaction(
|
|
312
|
+
() => translation.value,
|
|
313
|
+
() => {
|
|
314
|
+
if (!pagingEnabled) resetBoundary();
|
|
315
|
+
},
|
|
316
|
+
[pagingEnabled, resetBoundary]
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
function withProcessTranslation(translation: number) {
|
|
320
|
+
"worklet";
|
|
321
|
+
|
|
322
|
+
if (!loop && !overscrollEnabled) {
|
|
323
|
+
const limit = getLimit();
|
|
324
|
+
return Math.min(0, Math.max(-limit, translation));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return translation;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const onGestureStart = useCallback(
|
|
331
|
+
(_: PanGestureHandlerEventPayload) => {
|
|
332
|
+
"worklet";
|
|
333
|
+
// Use resolvedSize.value (SharedValue) instead of size (React state)
|
|
334
|
+
// to avoid race condition where sizeReady is true but size is still 0
|
|
335
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
336
|
+
const totalSpan = variableSize ? sizeResolver.total.value : 0;
|
|
337
|
+
const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
|
|
338
|
+
if (!ready) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
touching.value = true;
|
|
342
|
+
validStart.value = true;
|
|
343
|
+
onScrollStart && scheduleOnRN(onScrollStart);
|
|
344
|
+
|
|
345
|
+
max.value = variableSize ? sizeResolver.offsetAt(maxPage - 1) : (maxPage - 1) * currentSize;
|
|
346
|
+
if (!loop && !overscrollEnabled) max.value = getLimit();
|
|
347
|
+
|
|
348
|
+
panOffset.value = translation.value;
|
|
349
|
+
},
|
|
350
|
+
[
|
|
351
|
+
max,
|
|
352
|
+
maxPage,
|
|
353
|
+
loop,
|
|
354
|
+
touching,
|
|
355
|
+
panOffset,
|
|
356
|
+
validStart,
|
|
357
|
+
translation,
|
|
358
|
+
overscrollEnabled,
|
|
359
|
+
getLimit,
|
|
360
|
+
onScrollStart,
|
|
361
|
+
sizeReady,
|
|
362
|
+
resolvedSize,
|
|
363
|
+
variableSize,
|
|
364
|
+
sizeResolver,
|
|
365
|
+
]
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const onGestureUpdate = useCallback(
|
|
369
|
+
(e: PanGestureHandlerEventPayload) => {
|
|
370
|
+
"worklet";
|
|
371
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
372
|
+
const totalSpan = variableSize ? sizeResolver.total.value : 0;
|
|
373
|
+
const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
|
|
374
|
+
if (!ready) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (panOffset.value === undefined) {
|
|
378
|
+
// This may happen if `onGestureStart` is called as a part of the
|
|
379
|
+
// JS thread (instead of the UI thread / worklet). If so, when
|
|
380
|
+
// `onGestureStart` sets panOffset.value, the set will be asynchronous,
|
|
381
|
+
// and so it may not actually occur before `onGestureUpdate` is called.
|
|
382
|
+
//
|
|
383
|
+
// Keeping this value as `undefined` when it is not active protects us
|
|
384
|
+
// from the situation where we may use the previous value for panOffset
|
|
385
|
+
// instead; this would cause a visual flicker in the carousel.
|
|
386
|
+
|
|
387
|
+
// console.warn("onGestureUpdate: panOffset is undefined");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (validStart.value) {
|
|
392
|
+
validStart.value = false;
|
|
393
|
+
cancelAnimation(translation);
|
|
394
|
+
}
|
|
395
|
+
touching.value = true;
|
|
396
|
+
const { translationX, translationY } = e;
|
|
397
|
+
|
|
398
|
+
let panTranslation = isHorizontal.value ? translationX : translationY;
|
|
399
|
+
|
|
400
|
+
if (fixedDirection === "negative") panTranslation = -Math.abs(panTranslation);
|
|
401
|
+
else if (fixedDirection === "positive") panTranslation = +Math.abs(panTranslation);
|
|
402
|
+
|
|
403
|
+
translation.value = computeGestureTranslation({
|
|
404
|
+
loop,
|
|
405
|
+
overscrollEnabled: overscrollEnabled ?? true,
|
|
406
|
+
currentTranslation: translation.value,
|
|
407
|
+
panOffset: panOffset.value,
|
|
408
|
+
panTranslation,
|
|
409
|
+
max: max.value,
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
[
|
|
413
|
+
isHorizontal,
|
|
414
|
+
max,
|
|
415
|
+
panOffset,
|
|
416
|
+
loop,
|
|
417
|
+
overscrollEnabled,
|
|
418
|
+
fixedDirection,
|
|
419
|
+
translation,
|
|
420
|
+
validStart,
|
|
421
|
+
touching,
|
|
422
|
+
sizeReady,
|
|
423
|
+
resolvedSize,
|
|
424
|
+
variableSize,
|
|
425
|
+
sizeResolver,
|
|
426
|
+
]
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const onGestureEnd = useCallback(
|
|
430
|
+
(e: GestureStateChangeEvent<PanGestureHandlerEventPayload>, _success: boolean) => {
|
|
431
|
+
"worklet";
|
|
432
|
+
// Use resolvedSize.value (SharedValue) instead of size (React state)
|
|
433
|
+
// to avoid race condition where sizeReady is true but size is still 0
|
|
434
|
+
const currentSize = resolvedSize.value ?? 0;
|
|
435
|
+
const totalSpan = variableSize ? sizeResolver.total.value : 0;
|
|
436
|
+
const ready = variableSize ? totalSpan > 0 : sizeReady.value && currentSize > 0;
|
|
437
|
+
if (!ready) {
|
|
438
|
+
panOffset.value = undefined;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (panOffset.value === undefined) {
|
|
443
|
+
// console.warn("onGestureEnd: panOffset is undefined");
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const { velocityX, velocityY, translationX, translationY } = e;
|
|
448
|
+
const scrollEndVelocityValue = isHorizontal.value ? velocityX : velocityY;
|
|
449
|
+
scrollEndVelocity.value = scrollEndVelocityValue; // may update async: see https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue#remarks
|
|
450
|
+
|
|
451
|
+
let panTranslation = isHorizontal.value ? translationX : translationY;
|
|
452
|
+
|
|
453
|
+
if (fixedDirection === "negative") panTranslation = -Math.abs(panTranslation);
|
|
454
|
+
else if (fixedDirection === "positive") panTranslation = +Math.abs(panTranslation);
|
|
455
|
+
|
|
456
|
+
scrollEndTranslation.value = panTranslation; // may update async: see https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue#remarks
|
|
457
|
+
|
|
458
|
+
const totalTranslation = scrollEndVelocityValue + panTranslation;
|
|
459
|
+
|
|
460
|
+
// Snap to the item-aligned target nearest a particular pixel offset.
|
|
461
|
+
// In uniform mode this is `round(target / size) * size`; in variable
|
|
462
|
+
// mode it's `offsetAt(indexAt(target))` (already item-aligned).
|
|
463
|
+
const snapToItemAlignedOffset = (rawTarget: number): number => {
|
|
464
|
+
if (variableSize) {
|
|
465
|
+
const idx = sizeResolver.indexAt(-rawTarget);
|
|
466
|
+
return -sizeResolver.offsetAt(idx);
|
|
467
|
+
}
|
|
468
|
+
return Math.round(rawTarget / currentSize) * currentSize;
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* If the maximum scroll distance is set and the translation `exceeds the maximum scroll distance`,
|
|
473
|
+
* the carousel will keep the view at the current position.
|
|
474
|
+
*/
|
|
475
|
+
if (
|
|
476
|
+
maxScrollDistancePerSwipeIsSet &&
|
|
477
|
+
Math.abs(totalTranslation) > maxScrollDistancePerSwipe
|
|
478
|
+
) {
|
|
479
|
+
const target = panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation);
|
|
480
|
+
translation.value = withSpring(
|
|
481
|
+
withProcessTranslation(snapToItemAlignedOffset(target)),
|
|
482
|
+
onScrollEnd
|
|
483
|
+
);
|
|
484
|
+
} else if (
|
|
485
|
+
/**
|
|
486
|
+
* If the minimum scroll distance is set and the translation `didn't exceeds the minimum scroll distance`,
|
|
487
|
+
* the carousel will keep the view at the current position.
|
|
488
|
+
*/
|
|
489
|
+
minScrollDistancePerSwipeIsSet &&
|
|
490
|
+
Math.abs(totalTranslation) < minScrollDistancePerSwipe
|
|
491
|
+
) {
|
|
492
|
+
const target = panOffset.value + minScrollDistancePerSwipe * Math.sign(totalTranslation);
|
|
493
|
+
translation.value = withSpring(
|
|
494
|
+
withProcessTranslation(snapToItemAlignedOffset(target)),
|
|
495
|
+
onScrollEnd
|
|
496
|
+
);
|
|
497
|
+
} else {
|
|
498
|
+
endWithSpring(panTranslation, scrollEndVelocityValue, onScrollEnd);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (!loop) touching.value = false;
|
|
502
|
+
|
|
503
|
+
panOffset.value = undefined;
|
|
504
|
+
},
|
|
505
|
+
[
|
|
506
|
+
loop,
|
|
507
|
+
touching,
|
|
508
|
+
panOffset,
|
|
509
|
+
translation,
|
|
510
|
+
isHorizontal,
|
|
511
|
+
scrollEndVelocity,
|
|
512
|
+
scrollEndTranslation,
|
|
513
|
+
fixedDirection,
|
|
514
|
+
maxScrollDistancePerSwipeIsSet,
|
|
515
|
+
maxScrollDistancePerSwipe,
|
|
516
|
+
minScrollDistancePerSwipeIsSet,
|
|
517
|
+
minScrollDistancePerSwipe,
|
|
518
|
+
endWithSpring,
|
|
519
|
+
withSpring,
|
|
520
|
+
onScrollEnd,
|
|
521
|
+
sizeReady,
|
|
522
|
+
resolvedSize,
|
|
523
|
+
variableSize,
|
|
524
|
+
sizeResolver,
|
|
525
|
+
]
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
const gesture = usePanGestureProxy({
|
|
529
|
+
onConfigurePanGesture,
|
|
530
|
+
onGestureStart,
|
|
531
|
+
onGestureUpdate,
|
|
532
|
+
onGestureEnd,
|
|
533
|
+
options: { enabled },
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
const onLayout = React.useCallback(
|
|
537
|
+
(e: LayoutChangeEvent) => {
|
|
538
|
+
"worklet";
|
|
539
|
+
|
|
540
|
+
const measuredWidth = e.nativeEvent.layout.width;
|
|
541
|
+
const measuredHeight = e.nativeEvent.layout.height;
|
|
542
|
+
const measuredSize = Math.round((vertical ? measuredHeight : measuredWidth) || 0);
|
|
543
|
+
|
|
544
|
+
if (!sizeExplicit && measuredSize > 0) {
|
|
545
|
+
const current = resolvedSize.value ?? 0;
|
|
546
|
+
if (Math.abs(current - measuredSize) > 0) {
|
|
547
|
+
sizePhase.value = current > 0 ? "updating" : sizePhase.value;
|
|
548
|
+
resolvedSize.value = measuredSize;
|
|
549
|
+
sizePhase.value = "ready";
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
updateContainerSize({
|
|
554
|
+
width: measuredWidth,
|
|
555
|
+
height: measuredHeight,
|
|
556
|
+
});
|
|
557
|
+
},
|
|
558
|
+
[updateContainerSize, resolvedSize, sizePhase, vertical, sizeExplicit]
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
return (
|
|
562
|
+
<GestureDetector gesture={gesture}>
|
|
563
|
+
<Animated.View
|
|
564
|
+
ref={containerRef}
|
|
565
|
+
testID={testID}
|
|
566
|
+
style={style}
|
|
567
|
+
onTouchStart={onTouchBegin}
|
|
568
|
+
onTouchEnd={onTouchEnd}
|
|
569
|
+
onLayout={onLayout}
|
|
570
|
+
>
|
|
571
|
+
{props.children}
|
|
572
|
+
</Animated.View>
|
|
573
|
+
</GestureDetector>
|
|
574
|
+
);
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
export const ScrollViewGesture = IScrollViewGesture;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { render } from "@testing-library/react-native";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
|
|
5
|
+
|
|
6
|
+
describe("Carousel Animations", () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.useFakeTimers();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
jest.runOnlyPendingTimers();
|
|
13
|
+
jest.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("animates opacity with timing", () => {
|
|
17
|
+
const TestComponent: FC<{ value: number }> = ({ value }) => {
|
|
18
|
+
const sharedValue = useSharedValue(value);
|
|
19
|
+
|
|
20
|
+
// Using withTiming to wrap the value change, so it can be tested
|
|
21
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
22
|
+
opacity: withTiming(sharedValue.value, { duration: 500 }),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// When the prop changes, update the shared value
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
sharedValue.value = value;
|
|
28
|
+
}, [value, sharedValue]);
|
|
29
|
+
|
|
30
|
+
return <Animated.View testID="animated-view" style={animatedStyle} />;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const initialValue = 0;
|
|
34
|
+
const updatedValue = 1;
|
|
35
|
+
|
|
36
|
+
const { getByTestId, rerender } = render(<TestComponent value={initialValue} />);
|
|
37
|
+
const view = getByTestId("animated-view");
|
|
38
|
+
|
|
39
|
+
// Verify the initial state
|
|
40
|
+
expect(view).toHaveAnimatedStyle({ opacity: initialValue });
|
|
41
|
+
|
|
42
|
+
// Trigger the value change
|
|
43
|
+
rerender(<TestComponent value={updatedValue} />);
|
|
44
|
+
|
|
45
|
+
// Advance the animation to the middle point
|
|
46
|
+
jest.advanceTimersByTime(250);
|
|
47
|
+
expect(view).toHaveAnimatedStyle({ opacity: 0.5 }); // Animation middle value
|
|
48
|
+
|
|
49
|
+
// Advance the animation to the completion
|
|
50
|
+
jest.advanceTimersByTime(250);
|
|
51
|
+
expect(view).toHaveAnimatedStyle({ opacity: updatedValue }); // Animation completion
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EasingFunction } from "react-native-reanimated";
|
|
2
|
+
import { Easing as _Easing } from "react-native-reanimated";
|
|
3
|
+
|
|
4
|
+
export enum DATA_LENGTH {
|
|
5
|
+
SINGLE_ITEM = 1,
|
|
6
|
+
DOUBLE_ITEM = 2,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Easing = {
|
|
10
|
+
easeOutQuart: _Easing.bezier(0.25, 1, 0.5, 1) as unknown as EasingFunction,
|
|
11
|
+
};
|