@fountain-ui/lab 2.0.0-beta.15 → 2.0.0-beta.18
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/build/commonjs/Carousel/Carousel.js +11 -20
- package/build/commonjs/Carousel/Carousel.js.map +1 -1
- package/build/commonjs/Carousel/CarouselProps.js.map +1 -1
- package/build/commonjs/Carousel/animation/createDefaultScrollAnimation.js +2 -2
- package/build/commonjs/Carousel/animation/createDefaultScrollAnimation.js.map +1 -1
- package/build/commonjs/Carousel/components/ItemView.js +2 -2
- package/build/commonjs/Carousel/components/ItemView.js.map +1 -1
- package/build/commonjs/Carousel/components/ScrollViewGesture.js +4 -3
- package/build/commonjs/Carousel/components/ScrollViewGesture.js.map +1 -1
- package/build/commonjs/Carousel/hooks/index.js +3 -3
- package/build/commonjs/Carousel/hooks/index.js.map +1 -1
- package/build/commonjs/Carousel/hooks/useIndexController.js +28 -12
- package/build/commonjs/Carousel/hooks/useIndexController.js.map +1 -1
- package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js +3 -3
- package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js.map +1 -1
- package/build/commonjs/Carousel/hooks/{usePagingAnimation.js → usePagingAnimator.js} +45 -21
- package/build/commonjs/Carousel/hooks/usePagingAnimator.js.map +1 -0
- package/build/commonjs/Carousel/types.js +1 -0
- package/build/commonjs/Carousel/types.js.map +1 -1
- package/build/module/Carousel/Carousel.js +13 -19
- package/build/module/Carousel/Carousel.js.map +1 -1
- package/build/module/Carousel/CarouselProps.js.map +1 -1
- package/build/module/Carousel/animation/createDefaultScrollAnimation.js +2 -2
- package/build/module/Carousel/animation/createDefaultScrollAnimation.js.map +1 -1
- package/build/module/Carousel/components/ItemView.js +2 -2
- package/build/module/Carousel/components/ItemView.js.map +1 -1
- package/build/module/Carousel/components/ScrollViewGesture.js +4 -3
- package/build/module/Carousel/components/ScrollViewGesture.js.map +1 -1
- package/build/module/Carousel/hooks/index.js +1 -1
- package/build/module/Carousel/hooks/index.js.map +1 -1
- package/build/module/Carousel/hooks/useIndexController.js +28 -13
- package/build/module/Carousel/hooks/useIndexController.js.map +1 -1
- package/build/module/Carousel/hooks/useItemVisibilityStore.js +3 -3
- package/build/module/Carousel/hooks/useItemVisibilityStore.js.map +1 -1
- package/build/module/Carousel/hooks/{usePagingAnimation.js → usePagingAnimator.js} +44 -21
- package/build/module/Carousel/hooks/usePagingAnimator.js.map +1 -0
- package/build/module/Carousel/types.js +1 -0
- package/build/module/Carousel/types.js.map +1 -1
- package/build/typescript/Carousel/CarouselProps.d.ts +2 -2
- package/build/typescript/Carousel/components/ScrollViewGesture.d.ts +1 -1
- package/build/typescript/Carousel/hooks/index.d.ts +1 -1
- package/build/typescript/Carousel/hooks/useIndexController.d.ts +3 -1
- package/build/typescript/Carousel/hooks/useItemVisibilityStore.d.ts +2 -5
- package/build/typescript/Carousel/hooks/{usePagingAnimation.d.ts → usePagingAnimator.d.ts} +6 -5
- package/build/typescript/Carousel/types.d.ts +14 -1
- package/package.json +2 -2
- package/src/Carousel/Carousel.tsx +12 -20
- package/src/Carousel/CarouselProps.ts +9 -2
- package/src/Carousel/animation/createDefaultScrollAnimation.ts +2 -2
- package/src/Carousel/components/ItemView.tsx +2 -2
- package/src/Carousel/components/ScrollViewGesture.tsx +8 -4
- package/src/Carousel/hooks/index.ts +1 -1
- package/src/Carousel/hooks/useIndexController.tsx +30 -19
- package/src/Carousel/hooks/useItemVisibilityStore.ts +5 -9
- package/src/Carousel/hooks/{usePagingAnimation.ts → usePagingAnimator.ts} +53 -25
- package/src/Carousel/types.ts +19 -1
- package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +0 -1
- package/build/module/Carousel/hooks/usePagingAnimation.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["directions"],"sources":["types.ts"],"sourcesContent":["import type { ReactElement } from 'react';\nimport type { Animated, ViewProps } from 'react-native';\n\nconst directions = ['next', 'prev', 'stay'] as const;\n\nexport type PagingDirection = (typeof directions)[number];\n\nexport type ItemHeight = number | 'auto';\n\nexport interface RenderItem<T> {\n (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;\n}\n\nexport interface CreateScrollAnimation {\n (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;\n}\n\nexport interface CreateItemStyle {\n (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;\n}\n\nexport interface GetCurrentIndex {\n (): number;\n}\n\nexport interface IndexController {\n getCurrentIndex: GetCurrentIndex;\n lastIndex: number;\n
|
|
1
|
+
{"version":3,"names":["directions","scrollStates"],"sources":["types.ts"],"sourcesContent":["import type { ReactElement } from 'react';\nimport type { Animated, ViewProps } from 'react-native';\n\nconst directions = ['next', 'prev', 'stay'] as const;\n\nexport type PagingDirection = (typeof directions)[number];\n\nconst scrollStates = ['idle', 'dragging', 'interrupted'] as const;\n\nexport type ScrollState = (typeof scrollStates)[number];\n\nexport type ItemHeight = number | 'auto';\n\nexport interface RenderItem<T> {\n (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;\n}\n\nexport interface CreateScrollAnimation {\n (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;\n}\n\nexport interface CreateItemStyle {\n (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;\n}\n\nexport interface GetCurrentIndex {\n (): number;\n}\n\nexport interface OnIndexChange {\n (itemIndex: number): void;\n}\n\nexport interface OnPositionChange {\n (position: number): void;\n}\n\nexport interface ScrollStateChangeEvent {\n offset: number;\n state: ScrollState;\n}\n\nexport interface IndexController {\n getCurrentIndex: GetCurrentIndex;\n lastIndex: number;\n notifyScrollStateHasChanged: (event: ScrollStateChangeEvent) => void;\n}\n\nexport type PagingAnimationType = 'directional' | 'index';\n\nexport interface BasePagingAnimationConfig {\n animated?: boolean;\n lastGestureTranslationX?: number;\n}\n\nexport interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {\n direction: PagingDirection;\n isOriginatedFromGesture?: boolean;\n}\n\nexport interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {\n index: number;\n}\n\nexport type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;\n\nexport interface StartPagingAnimation {\n (type: PagingAnimationType, config: PagingAnimationConfig): void;\n}\n\nexport type VisibleIndexRanges = Array<[number, number]>;\n\nexport interface StoreSubscription {\n (): void;\n}\n\nexport interface ItemVisibilityStore {\n dispatch: (ranges: VisibleIndexRanges) => void;\n subscribe: (listener: (ranges: VisibleIndexRanges) => void) => StoreSubscription;\n removeAllListeners: () => void;\n}\n\nexport interface AutoplayController {\n pause: () => void;\n resume: () => void;\n}\n\nexport interface ScrollToOption {\n index: number;\n animated?: boolean;\n}\n\nexport interface CarouselInstance {\n /**\n * Get current visible item index.\n */\n getCurrentIndex: GetCurrentIndex;\n\n /**\n * Scroll to next visible item.\n */\n next: () => void;\n\n /**\n * Scroll to previous visible item.\n */\n prev: () => void;\n\n /**\n * Scroll to desired indexed item.\n * Invalid index is ignored.\n */\n scrollTo: (option: ScrollToOption) => void;\n}\n"],"mappings":"AAGA,MAAMA,UAAU,GAAG,CAAC,MAAD,EAAS,MAAT,EAAiB,MAAjB,CAAnB;AAIA,MAAMC,YAAY,GAAG,CAAC,MAAD,EAAS,UAAT,EAAqB,aAArB,CAArB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
2
|
import type { ComponentProps } from '@fountain-ui/core';
|
|
3
|
-
import type { CarouselInstance, CreateItemStyle, CreateScrollAnimation, ItemHeight, RenderItem } from './types';
|
|
3
|
+
import type { CarouselInstance, CreateItemStyle, CreateScrollAnimation, ItemHeight, OnIndexChange, RenderItem } from './types';
|
|
4
4
|
export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
5
5
|
ref?: RefObject<CarouselInstance>;
|
|
6
6
|
/**
|
|
@@ -54,7 +54,7 @@ export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
|
54
54
|
/**
|
|
55
55
|
* Callback fired when an index is changed.
|
|
56
56
|
*/
|
|
57
|
-
onIndexChange?:
|
|
57
|
+
onIndexChange?: OnIndexChange;
|
|
58
58
|
/**
|
|
59
59
|
* Takes an item from data and renders it into the list.
|
|
60
60
|
*/
|
|
@@ -4,8 +4,8 @@ import type { AutoplayController, StartPagingAnimation } from '../types';
|
|
|
4
4
|
export interface ScrollViewGestureProps {
|
|
5
5
|
autoplayController: AutoplayController;
|
|
6
6
|
children: ReactNode;
|
|
7
|
+
gestureTranslationX: Animated.Value;
|
|
7
8
|
interruptAnimation: () => void;
|
|
8
|
-
translateX: Animated.Value;
|
|
9
9
|
scrollEnabled: boolean;
|
|
10
10
|
startPagingAnimation: StartPagingAnimation;
|
|
11
11
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as useAutoplayController } from './useAutoplayController';
|
|
2
2
|
export { default as useIndexController } from './useIndexController';
|
|
3
3
|
export { default as useLoopedData } from './useLoopedData';
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as usePagingAnimator } from './usePagingAnimator';
|
|
5
5
|
export { default as useItemVisibilityStore } from './useItemVisibilityStore';
|
|
@@ -2,7 +2,9 @@ import type { IndexController } from '../types';
|
|
|
2
2
|
export interface UseIndexControllerParameters {
|
|
3
3
|
initialIndex: number;
|
|
4
4
|
itemWidth: number;
|
|
5
|
+
numberOfData: number;
|
|
5
6
|
numberOfOriginalData: number;
|
|
6
|
-
onIndexChange?: (
|
|
7
|
+
onIndexChange?: (itemIndex: number) => void;
|
|
8
|
+
onPositionChange?: (position: number) => void;
|
|
7
9
|
}
|
|
8
10
|
export default function useIndexController(params: UseIndexControllerParameters): IndexController;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import type { ItemVisibilityStore } from '../types';
|
|
1
|
+
import type { ItemVisibilityStore, OnPositionChange } from '../types';
|
|
2
2
|
export interface Parameters {
|
|
3
3
|
initialIndex: number;
|
|
4
4
|
numberOfData: number;
|
|
5
5
|
windowSize: number;
|
|
6
6
|
}
|
|
7
|
-
export
|
|
8
|
-
(newIndex: number): void;
|
|
9
|
-
}
|
|
10
|
-
export default function useItemVisibilityStore(params: Parameters): [ItemVisibilityStore, OnIndexChange];
|
|
7
|
+
export default function useItemVisibilityStore(params: Parameters): [ItemVisibilityStore, OnPositionChange];
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Animated } from 'react-native';
|
|
2
2
|
import type { CreateScrollAnimation, IndexController, StartPagingAnimation } from '../types';
|
|
3
|
-
export interface
|
|
3
|
+
export interface PagingAnimatorConfig {
|
|
4
4
|
createScrollAnimation: CreateScrollAnimation;
|
|
5
5
|
itemWidth: number;
|
|
6
6
|
indexController: IndexController;
|
|
7
|
+
initialIndex: number;
|
|
7
8
|
loop: boolean;
|
|
8
9
|
numberOfData: number;
|
|
9
|
-
offsetX: Animated.Value;
|
|
10
|
-
translateX: Animated.Value;
|
|
11
10
|
}
|
|
12
|
-
export interface
|
|
11
|
+
export interface PagingAnimator {
|
|
12
|
+
gestureTranslationX: Animated.Value;
|
|
13
|
+
globalInterpolation: Animated.AnimatedInterpolation;
|
|
13
14
|
interruptAnimation: () => void;
|
|
14
15
|
startPagingAnimation: StartPagingAnimation;
|
|
15
16
|
}
|
|
16
|
-
export default function
|
|
17
|
+
export default function usePagingAnimator(config: PagingAnimatorConfig): PagingAnimator;
|
|
@@ -2,6 +2,8 @@ import type { ReactElement } from 'react';
|
|
|
2
2
|
import type { Animated, ViewProps } from 'react-native';
|
|
3
3
|
declare const directions: readonly ["next", "prev", "stay"];
|
|
4
4
|
export declare type PagingDirection = (typeof directions)[number];
|
|
5
|
+
declare const scrollStates: readonly ["idle", "dragging", "interrupted"];
|
|
6
|
+
export declare type ScrollState = (typeof scrollStates)[number];
|
|
5
7
|
export declare type ItemHeight = number | 'auto';
|
|
6
8
|
export interface RenderItem<T> {
|
|
7
9
|
(info: {
|
|
@@ -19,14 +21,25 @@ export interface CreateItemStyle {
|
|
|
19
21
|
export interface GetCurrentIndex {
|
|
20
22
|
(): number;
|
|
21
23
|
}
|
|
24
|
+
export interface OnIndexChange {
|
|
25
|
+
(itemIndex: number): void;
|
|
26
|
+
}
|
|
27
|
+
export interface OnPositionChange {
|
|
28
|
+
(position: number): void;
|
|
29
|
+
}
|
|
30
|
+
export interface ScrollStateChangeEvent {
|
|
31
|
+
offset: number;
|
|
32
|
+
state: ScrollState;
|
|
33
|
+
}
|
|
22
34
|
export interface IndexController {
|
|
23
35
|
getCurrentIndex: GetCurrentIndex;
|
|
24
36
|
lastIndex: number;
|
|
25
|
-
|
|
37
|
+
notifyScrollStateHasChanged: (event: ScrollStateChangeEvent) => void;
|
|
26
38
|
}
|
|
27
39
|
export declare type PagingAnimationType = 'directional' | 'index';
|
|
28
40
|
export interface BasePagingAnimationConfig {
|
|
29
41
|
animated?: boolean;
|
|
42
|
+
lastGestureTranslationX?: number;
|
|
30
43
|
}
|
|
31
44
|
export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
32
45
|
direction: PagingDirection;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fountain-ui/lab",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.18",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Fountain-UI Team",
|
|
6
6
|
"description": "Incubator for Fountain-UI React components.",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "8ca13c00c2424b287a26c7cb62619c2c0a9596df"
|
|
74
74
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef, memo,
|
|
2
|
-
import { Animated } from 'react-native';
|
|
1
|
+
import React, { forwardRef, memo, useImperativeHandle, useMemo } from 'react';
|
|
3
2
|
import ViewabilityTrackerView from '../ViewabilityTrackerView';
|
|
4
3
|
import type CarouselProps from './CarouselProps';
|
|
5
4
|
import type { CarouselInstance } from './types';
|
|
@@ -8,7 +7,7 @@ import {
|
|
|
8
7
|
useIndexController,
|
|
9
8
|
useItemVisibilityStore,
|
|
10
9
|
useLoopedData,
|
|
11
|
-
|
|
10
|
+
usePagingAnimator,
|
|
12
11
|
} from './hooks';
|
|
13
12
|
import { createDefaultItemStyle, createDefaultScrollAnimation } from './animation';
|
|
14
13
|
import { InternalContext, RootView, ScrollViewGesture } from './components';
|
|
@@ -25,7 +24,7 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
25
24
|
itemHeight,
|
|
26
25
|
itemWidth,
|
|
27
26
|
loop = false,
|
|
28
|
-
onIndexChange
|
|
27
|
+
onIndexChange,
|
|
29
28
|
renderItem,
|
|
30
29
|
scrollEnabled = true,
|
|
31
30
|
style,
|
|
@@ -34,42 +33,35 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
34
33
|
|
|
35
34
|
const data = useLoopedData(originalData, loop);
|
|
36
35
|
|
|
37
|
-
const
|
|
38
|
-
const offsetX = useRef(new Animated.Value(initialTx)).current;
|
|
39
|
-
const translateX = useRef(new Animated.Value(0)).current;
|
|
40
|
-
const globalInterpolation = Animated.add(offsetX, translateX);
|
|
41
|
-
|
|
42
|
-
const [itemVisibilityStore, onIndexChange] = useItemVisibilityStore({
|
|
36
|
+
const [itemVisibilityStore, onPositionChange] = useItemVisibilityStore({
|
|
43
37
|
initialIndex,
|
|
44
38
|
numberOfData: data.length,
|
|
45
39
|
windowSize,
|
|
46
40
|
});
|
|
47
41
|
|
|
48
|
-
const handleIndexChange = useCallback((newIndex: number) => {
|
|
49
|
-
onIndexChange(newIndex);
|
|
50
|
-
onIndexChangeProp?.(newIndex);
|
|
51
|
-
}, [onIndexChange, onIndexChangeProp]);
|
|
52
|
-
|
|
53
42
|
const indexController = useIndexController({
|
|
54
43
|
initialIndex,
|
|
55
44
|
itemWidth,
|
|
45
|
+
numberOfData: data.length,
|
|
56
46
|
numberOfOriginalData: originalData.length,
|
|
57
|
-
onIndexChange
|
|
47
|
+
onIndexChange,
|
|
48
|
+
onPositionChange,
|
|
58
49
|
});
|
|
59
50
|
|
|
60
51
|
const { getCurrentIndex } = indexController;
|
|
61
52
|
|
|
62
53
|
const {
|
|
54
|
+
gestureTranslationX,
|
|
55
|
+
globalInterpolation,
|
|
63
56
|
interruptAnimation,
|
|
64
57
|
startPagingAnimation,
|
|
65
|
-
} =
|
|
58
|
+
} = usePagingAnimator({
|
|
66
59
|
createScrollAnimation,
|
|
67
60
|
itemWidth,
|
|
68
61
|
indexController,
|
|
62
|
+
initialIndex,
|
|
69
63
|
loop,
|
|
70
64
|
numberOfData: data.length,
|
|
71
|
-
offsetX,
|
|
72
|
-
translateX,
|
|
73
65
|
});
|
|
74
66
|
|
|
75
67
|
const autoplayController = useAutoplayController({
|
|
@@ -122,8 +114,8 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
122
114
|
>
|
|
123
115
|
<ScrollViewGesture
|
|
124
116
|
autoplayController={autoplayController}
|
|
117
|
+
gestureTranslationX={gestureTranslationX}
|
|
125
118
|
interruptAnimation={interruptAnimation}
|
|
126
|
-
translateX={translateX}
|
|
127
119
|
scrollEnabled={scrollEnabled}
|
|
128
120
|
startPagingAnimation={startPagingAnimation}
|
|
129
121
|
>
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
2
|
import type { ComponentProps } from '@fountain-ui/core';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
CarouselInstance,
|
|
5
|
+
CreateItemStyle,
|
|
6
|
+
CreateScrollAnimation,
|
|
7
|
+
ItemHeight,
|
|
8
|
+
OnIndexChange,
|
|
9
|
+
RenderItem,
|
|
10
|
+
} from './types';
|
|
4
11
|
|
|
5
12
|
export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
6
13
|
ref?: RefObject<CarouselInstance>;
|
|
@@ -66,7 +73,7 @@ export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
|
66
73
|
/**
|
|
67
74
|
* Callback fired when an index is changed.
|
|
68
75
|
*/
|
|
69
|
-
onIndexChange?:
|
|
76
|
+
onIndexChange?: OnIndexChange;
|
|
70
77
|
|
|
71
78
|
/**
|
|
72
79
|
* Takes an item from data and renders it into the list.
|
|
@@ -6,8 +6,8 @@ export default function createDefaultScrollAnimation(
|
|
|
6
6
|
): Animated.CompositeAnimation {
|
|
7
7
|
return Animated.timing(animatedValue, {
|
|
8
8
|
toValue: toValue,
|
|
9
|
-
duration:
|
|
10
|
-
easing: Easing.bezier(0.
|
|
9
|
+
duration: 180,
|
|
10
|
+
easing: Easing.bezier(0.2, 0.2, 0.2, 1),
|
|
11
11
|
useNativeDriver: true,
|
|
12
12
|
});
|
|
13
13
|
};
|
|
@@ -32,7 +32,7 @@ export default function ItemView(props: ItemViewProps) {
|
|
|
32
32
|
|
|
33
33
|
const itemStyle = useMemo(
|
|
34
34
|
() => createItemStyle(interpolation, itemWidth),
|
|
35
|
-
[createItemStyle, interpolation],
|
|
35
|
+
[createItemStyle, interpolation, itemWidth],
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
useEffect(() => {
|
|
@@ -40,7 +40,7 @@ export default function ItemView(props: ItemViewProps) {
|
|
|
40
40
|
const nextVisible = ranges.some(([from, to]) => index >= from && index <= to);
|
|
41
41
|
setVisible(nextVisible);
|
|
42
42
|
});
|
|
43
|
-
}, [itemVisibilityStore]);
|
|
43
|
+
}, [index, itemVisibilityStore]);
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<Animated.View
|
|
@@ -8,8 +8,8 @@ import type { AutoplayController, PagingDirection, StartPagingAnimation } from '
|
|
|
8
8
|
export interface ScrollViewGestureProps {
|
|
9
9
|
autoplayController: AutoplayController;
|
|
10
10
|
children: ReactNode;
|
|
11
|
+
gestureTranslationX: Animated.Value,
|
|
11
12
|
interruptAnimation: () => void;
|
|
12
|
-
translateX: Animated.Value,
|
|
13
13
|
scrollEnabled: boolean;
|
|
14
14
|
startPagingAnimation: StartPagingAnimation;
|
|
15
15
|
}
|
|
@@ -40,7 +40,7 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
|
40
40
|
autoplayController,
|
|
41
41
|
children,
|
|
42
42
|
interruptAnimation,
|
|
43
|
-
|
|
43
|
+
gestureTranslationX,
|
|
44
44
|
scrollEnabled,
|
|
45
45
|
startPagingAnimation,
|
|
46
46
|
} = props;
|
|
@@ -54,7 +54,7 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
|
54
54
|
}, [interruptAnimation, pauseAutoplay]);
|
|
55
55
|
|
|
56
56
|
const handleGestureEvent = useCallback(Animated.event(
|
|
57
|
-
[{ nativeEvent: { translationX:
|
|
57
|
+
[{ nativeEvent: { translationX: gestureTranslationX } }],
|
|
58
58
|
{ useNativeDriver: true },
|
|
59
59
|
), []);
|
|
60
60
|
|
|
@@ -68,7 +68,11 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
|
68
68
|
|
|
69
69
|
startPagingAnimation(
|
|
70
70
|
'directional',
|
|
71
|
-
{
|
|
71
|
+
{
|
|
72
|
+
direction: direction,
|
|
73
|
+
isOriginatedFromGesture: true,
|
|
74
|
+
lastGestureTranslationX: translationX,
|
|
75
|
+
},
|
|
72
76
|
);
|
|
73
77
|
|
|
74
78
|
resumeAutoplay();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as useAutoplayController } from './useAutoplayController';
|
|
2
2
|
export { default as useIndexController } from './useIndexController';
|
|
3
3
|
export { default as useLoopedData } from './useLoopedData';
|
|
4
|
-
export { default as
|
|
4
|
+
export { default as usePagingAnimator } from './usePagingAnimator';
|
|
5
5
|
export { default as useItemVisibilityStore } from './useItemVisibilityStore';
|
|
@@ -1,46 +1,57 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useImperativeState } from '@fountain-ui/core';
|
|
2
3
|
import { mod } from '@fountain-ui/utils';
|
|
3
|
-
import type { IndexController } from '../types';
|
|
4
|
+
import type { IndexController, ScrollStateChangeEvent } from '../types';
|
|
4
5
|
|
|
5
6
|
export interface UseIndexControllerParameters {
|
|
6
7
|
initialIndex: number;
|
|
7
8
|
itemWidth: number;
|
|
9
|
+
numberOfData: number;
|
|
8
10
|
numberOfOriginalData: number;
|
|
9
|
-
onIndexChange?: (
|
|
11
|
+
onIndexChange?: (itemIndex: number) => void;
|
|
12
|
+
onPositionChange?: (position: number) => void;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export default function useIndexController(params: UseIndexControllerParameters): IndexController {
|
|
13
16
|
const {
|
|
14
17
|
initialIndex,
|
|
15
18
|
itemWidth,
|
|
19
|
+
numberOfData,
|
|
16
20
|
numberOfOriginalData,
|
|
17
21
|
onIndexChange,
|
|
22
|
+
onPositionChange,
|
|
18
23
|
} = params;
|
|
19
24
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
const currentIndex = useImperativeState(initialIndex);
|
|
26
|
+
const currentPosition = useImperativeState(initialIndex);
|
|
27
|
+
|
|
28
|
+
const notifyScrollStateHasChanged = useCallback(({ offset, state }: ScrollStateChangeEvent) => {
|
|
29
|
+
const normalized = -Math.round(offset / itemWidth);
|
|
30
|
+
const index = Math.floor(mod(normalized, numberOfOriginalData));
|
|
31
|
+
const position = Math.floor(mod(normalized, numberOfData));
|
|
32
|
+
|
|
33
|
+
if (state === 'idle' || state === 'interrupted') {
|
|
34
|
+
if (currentIndex.hasChanged()) {
|
|
35
|
+
onIndexChange?.(currentIndex.get());
|
|
36
|
+
}
|
|
37
|
+
if (currentPosition.hasChanged()) {
|
|
38
|
+
onPositionChange?.(currentPosition.get());
|
|
39
|
+
}
|
|
34
40
|
}
|
|
41
|
+
|
|
42
|
+
currentIndex.set(index);
|
|
43
|
+
currentPosition.set(position);
|
|
35
44
|
}, [
|
|
36
45
|
itemWidth,
|
|
46
|
+
numberOfData,
|
|
37
47
|
numberOfOriginalData,
|
|
38
48
|
onIndexChange,
|
|
49
|
+
onPositionChange,
|
|
39
50
|
]);
|
|
40
51
|
|
|
41
52
|
return {
|
|
42
|
-
getCurrentIndex,
|
|
53
|
+
getCurrentIndex: currentIndex.get,
|
|
43
54
|
lastIndex: numberOfOriginalData - 1,
|
|
44
|
-
|
|
55
|
+
notifyScrollStateHasChanged,
|
|
45
56
|
};
|
|
46
57
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { mod } from '@fountain-ui/utils';
|
|
3
|
-
import type { ItemVisibilityStore, VisibleIndexRanges } from '../types';
|
|
3
|
+
import type { ItemVisibilityStore, OnPositionChange, VisibleIndexRanges } from '../types';
|
|
4
4
|
|
|
5
5
|
export interface Parameters {
|
|
6
6
|
initialIndex: number;
|
|
@@ -8,10 +8,6 @@ export interface Parameters {
|
|
|
8
8
|
windowSize: number;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export interface OnIndexChange {
|
|
12
|
-
(newIndex: number): void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
class SimpleItemVisibilityStore implements ItemVisibilityStore {
|
|
16
12
|
|
|
17
13
|
private store: VisibleIndexRanges;
|
|
@@ -82,7 +78,7 @@ function makeVisibleIndexRanges(numberOfData: number, windowSize: number, index:
|
|
|
82
78
|
];
|
|
83
79
|
}
|
|
84
80
|
|
|
85
|
-
export default function useItemVisibilityStore(params: Parameters): [ItemVisibilityStore,
|
|
81
|
+
export default function useItemVisibilityStore(params: Parameters): [ItemVisibilityStore, OnPositionChange] {
|
|
86
82
|
const {
|
|
87
83
|
initialIndex,
|
|
88
84
|
numberOfData,
|
|
@@ -95,8 +91,8 @@ export default function useItemVisibilityStore(params: Parameters): [ItemVisibil
|
|
|
95
91
|
|
|
96
92
|
const store = useRef(new SimpleItemVisibilityStore(initialRange)).current;
|
|
97
93
|
|
|
98
|
-
const
|
|
99
|
-
const newRanges = makeVisibleIndexRanges(numberOfData, windowSize,
|
|
94
|
+
const onPositionChange: OnPositionChange = useCallback((position) => {
|
|
95
|
+
const newRanges = makeVisibleIndexRanges(numberOfData, windowSize, position);
|
|
100
96
|
|
|
101
97
|
store.dispatch(newRanges);
|
|
102
98
|
}, [numberOfData, windowSize]);
|
|
@@ -107,5 +103,5 @@ export default function useItemVisibilityStore(params: Parameters): [ItemVisibil
|
|
|
107
103
|
};
|
|
108
104
|
}, []);
|
|
109
105
|
|
|
110
|
-
return [store,
|
|
106
|
+
return [store, onPositionChange];
|
|
111
107
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useRef } from 'react';
|
|
1
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
2
2
|
import { Animated } from 'react-native';
|
|
3
3
|
import type {
|
|
4
4
|
CreateScrollAnimation,
|
|
@@ -11,17 +11,18 @@ import type {
|
|
|
11
11
|
StartPagingAnimation,
|
|
12
12
|
} from '../types';
|
|
13
13
|
|
|
14
|
-
export interface
|
|
14
|
+
export interface PagingAnimatorConfig {
|
|
15
15
|
createScrollAnimation: CreateScrollAnimation;
|
|
16
16
|
itemWidth: number;
|
|
17
17
|
indexController: IndexController;
|
|
18
|
+
initialIndex: number;
|
|
18
19
|
loop: boolean;
|
|
19
20
|
numberOfData: number;
|
|
20
|
-
offsetX: Animated.Value;
|
|
21
|
-
translateX: Animated.Value;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
export interface
|
|
23
|
+
export interface PagingAnimator {
|
|
24
|
+
gestureTranslationX: Animated.Value;
|
|
25
|
+
globalInterpolation: Animated.AnimatedInterpolation;
|
|
25
26
|
interruptAnimation: () => void;
|
|
26
27
|
startPagingAnimation: StartPagingAnimation;
|
|
27
28
|
}
|
|
@@ -54,23 +55,34 @@ function toValueCompensator(itemWidth: number) {
|
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
export default function
|
|
58
|
+
export default function usePagingAnimator(config: PagingAnimatorConfig): PagingAnimator {
|
|
58
59
|
const {
|
|
59
60
|
createScrollAnimation,
|
|
60
61
|
itemWidth,
|
|
61
62
|
indexController,
|
|
63
|
+
initialIndex,
|
|
62
64
|
loop,
|
|
63
65
|
numberOfData,
|
|
64
|
-
|
|
65
|
-
translateX,
|
|
66
|
-
} = params;
|
|
66
|
+
} = config;
|
|
67
67
|
|
|
68
68
|
const {
|
|
69
69
|
getCurrentIndex,
|
|
70
70
|
lastIndex,
|
|
71
|
-
|
|
71
|
+
notifyScrollStateHasChanged,
|
|
72
72
|
} = indexController;
|
|
73
73
|
|
|
74
|
+
const initialOffsetX = itemWidth * initialIndex;
|
|
75
|
+
const accumulativeOffsetX = useRef(new Animated.Value(initialOffsetX)).current;
|
|
76
|
+
const animationOffsetX = useRef(new Animated.Value(0)).current;
|
|
77
|
+
const gestureTranslationX = useRef(new Animated.Value(0)).current;
|
|
78
|
+
|
|
79
|
+
const globalInterpolation = useMemo(() => {
|
|
80
|
+
return Animated.add(
|
|
81
|
+
Animated.add(accumulativeOffsetX, animationOffsetX),
|
|
82
|
+
gestureTranslationX,
|
|
83
|
+
);
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
74
86
|
const toValueRef = useRef<number>(0);
|
|
75
87
|
const currentOffsetRef = useRef<number>(0);
|
|
76
88
|
|
|
@@ -94,33 +106,33 @@ export default function usePagingAnimation(params: PagingAnimationParameters): U
|
|
|
94
106
|
const nextOffset = ensureOffsetBoundary(newOffset);
|
|
95
107
|
|
|
96
108
|
currentOffsetRef.current = nextOffset;
|
|
97
|
-
|
|
109
|
+
accumulativeOffsetX.setValue(nextOffset);
|
|
98
110
|
|
|
99
111
|
toValueRef.current = 0;
|
|
100
|
-
|
|
112
|
+
animationOffsetX.setValue(0);
|
|
101
113
|
}, [
|
|
102
114
|
ensureOffsetBoundary,
|
|
103
|
-
offsetX,
|
|
104
|
-
translateX,
|
|
105
115
|
]);
|
|
106
116
|
|
|
107
117
|
const interruptAnimation = useCallback(() => {
|
|
108
|
-
if (
|
|
109
|
-
// Performance optimization
|
|
118
|
+
if (!isAnimatingRef.current) {
|
|
110
119
|
return;
|
|
111
120
|
}
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
animationOffsetX.stopAnimation(lastValue => {
|
|
114
123
|
isAnimatingRef.current = false;
|
|
115
124
|
|
|
116
125
|
const prevOffset = currentOffsetRef.current;
|
|
117
126
|
const totalOffset = prevOffset + lastValue;
|
|
118
127
|
|
|
119
|
-
notifyOffsetHasChanged(totalOffset);
|
|
120
|
-
|
|
121
128
|
requireNewOffset(totalOffset);
|
|
129
|
+
|
|
130
|
+
notifyScrollStateHasChanged({ offset: totalOffset, state: 'interrupted' });
|
|
122
131
|
});
|
|
123
|
-
}, [
|
|
132
|
+
}, [
|
|
133
|
+
notifyScrollStateHasChanged,
|
|
134
|
+
requireNewOffset,
|
|
135
|
+
]);
|
|
124
136
|
|
|
125
137
|
const finalizeAnimation = useCallback(() => {
|
|
126
138
|
isAnimatingRef.current = false;
|
|
@@ -130,7 +142,12 @@ export default function usePagingAnimation(params: PagingAnimationParameters): U
|
|
|
130
142
|
const totalOffset = prevOffset + toValue;
|
|
131
143
|
|
|
132
144
|
requireNewOffset(totalOffset);
|
|
133
|
-
|
|
145
|
+
|
|
146
|
+
notifyScrollStateHasChanged({ offset: totalOffset, state: 'idle' });
|
|
147
|
+
}, [
|
|
148
|
+
notifyScrollStateHasChanged,
|
|
149
|
+
requireNewOffset,
|
|
150
|
+
]);
|
|
134
151
|
|
|
135
152
|
const startPagingAnimation = useCallback((type: PagingAnimationType, config: PagingAnimationConfig) => {
|
|
136
153
|
if (isAnimatingRef.current) {
|
|
@@ -196,8 +213,19 @@ export default function usePagingAnimation(params: PagingAnimationParameters): U
|
|
|
196
213
|
toValueRef.current = toValue;
|
|
197
214
|
isAnimatingRef.current = true;
|
|
198
215
|
|
|
216
|
+
notifyScrollStateHasChanged({
|
|
217
|
+
offset: currentOffsetRef.current + toValue,
|
|
218
|
+
state: 'dragging',
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const lastGestureTranslationX: number = configWithDefaults.lastGestureTranslationX ?? 0;
|
|
222
|
+
if (Number.isFinite(lastGestureTranslationX)) {
|
|
223
|
+
animationOffsetX.setValue(lastGestureTranslationX);
|
|
224
|
+
gestureTranslationX.setValue(0);
|
|
225
|
+
}
|
|
226
|
+
|
|
199
227
|
if (configWithDefaults.animated) {
|
|
200
|
-
const animation = createScrollAnimation(
|
|
228
|
+
const animation = createScrollAnimation(animationOffsetX, toValue);
|
|
201
229
|
|
|
202
230
|
animation.start(({ finished }) => {
|
|
203
231
|
if (finished) {
|
|
@@ -207,8 +235,6 @@ export default function usePagingAnimation(params: PagingAnimationParameters): U
|
|
|
207
235
|
} else {
|
|
208
236
|
finalizeAnimation();
|
|
209
237
|
}
|
|
210
|
-
|
|
211
|
-
notifyOffsetHasChanged(currentOffsetRef.current + toValue);
|
|
212
238
|
}, [
|
|
213
239
|
createScrollAnimation,
|
|
214
240
|
getCurrentIndex,
|
|
@@ -216,10 +242,12 @@ export default function usePagingAnimation(params: PagingAnimationParameters): U
|
|
|
216
242
|
itemWidth,
|
|
217
243
|
lastIndex,
|
|
218
244
|
loop,
|
|
219
|
-
|
|
245
|
+
notifyScrollStateHasChanged,
|
|
220
246
|
]);
|
|
221
247
|
|
|
222
248
|
return {
|
|
249
|
+
gestureTranslationX,
|
|
250
|
+
globalInterpolation,
|
|
223
251
|
interruptAnimation,
|
|
224
252
|
startPagingAnimation,
|
|
225
253
|
};
|