@fountain-ui/lab 2.0.0-beta.12 → 2.0.0-beta.15
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 +39 -37
- 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/animation/parallaxItemStyleFactory.js +15 -15
- package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js.map +1 -1
- package/build/commonjs/Carousel/components/InternalContext.js.map +1 -1
- package/build/commonjs/Carousel/components/ItemView.js +6 -4
- package/build/commonjs/Carousel/components/ItemView.js.map +1 -1
- package/build/commonjs/Carousel/components/RootView.js +21 -3
- package/build/commonjs/Carousel/components/RootView.js.map +1 -1
- package/build/commonjs/Carousel/components/ScrollViewGesture.js +18 -13
- package/build/commonjs/Carousel/components/ScrollViewGesture.js.map +1 -1
- package/build/commonjs/Carousel/{hooks → components}/useItemInterpolation.js +6 -4
- package/build/commonjs/Carousel/components/useItemInterpolation.js.map +1 -0
- package/build/commonjs/Carousel/hooks/index.js +0 -16
- package/build/commonjs/Carousel/hooks/index.js.map +1 -1
- package/build/commonjs/Carousel/hooks/useAutoplayController.js +4 -1
- package/build/commonjs/Carousel/hooks/useAutoplayController.js.map +1 -1
- package/build/commonjs/Carousel/hooks/useIndexController.js +15 -46
- package/build/commonjs/Carousel/hooks/useIndexController.js.map +1 -1
- package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js +12 -12
- package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js.map +1 -1
- package/build/commonjs/Carousel/hooks/usePagingAnimation.js +121 -69
- package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +1 -1
- package/build/commonjs/Carousel/tick.js +16 -0
- package/build/commonjs/Carousel/tick.js.map +1 -0
- package/build/commonjs/Carousel/types.js.map +1 -1
- package/build/commonjs/ViewPager/ChildrenMemoizedPage.js +53 -47
- package/build/commonjs/ViewPager/ChildrenMemoizedPage.js.map +1 -1
- package/build/commonjs/ViewPager/InternalContext.js +17 -0
- package/build/commonjs/ViewPager/InternalContext.js.map +1 -0
- package/build/commonjs/ViewPager/ViewPagerNative.js +74 -23
- package/build/commonjs/ViewPager/ViewPagerNative.js.map +1 -1
- package/build/commonjs/ViewPager/ViewPagerProps.js.map +1 -1
- package/build/commonjs/ViewPager/ViewPagerWeb.js +23 -12
- package/build/commonjs/ViewPager/ViewPagerWeb.js.map +1 -1
- package/build/commonjs/ViewPager/index.js.map +1 -1
- package/build/commonjs/ViewPager/types.js +6 -0
- package/build/commonjs/ViewPager/types.js.map +1 -0
- package/build/commonjs/ViewPager/usePageStore.js +35 -0
- package/build/commonjs/ViewPager/usePageStore.js.map +1 -0
- package/build/commonjs/ViewPager/utils.js.map +1 -1
- package/build/commonjs/ViewabilityTrackerView/measureViewability.js +6 -6
- package/build/commonjs/ViewabilityTrackerView/measureViewability.js.map +1 -1
- package/build/commonjs/hooks/useUnstableCollapsibleAppBar.js +1 -1
- package/build/commonjs/hooks/useUnstableCollapsibleAppBar.js.map +1 -1
- package/build/module/Carousel/Carousel.js +39 -39
- 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/animation/parallaxItemStyleFactory.js +15 -15
- package/build/module/Carousel/animation/parallaxItemStyleFactory.js.map +1 -1
- package/build/module/Carousel/components/InternalContext.js.map +1 -1
- package/build/module/Carousel/components/ItemView.js +5 -3
- package/build/module/Carousel/components/ItemView.js.map +1 -1
- package/build/module/Carousel/components/RootView.js +22 -4
- package/build/module/Carousel/components/RootView.js.map +1 -1
- package/build/module/Carousel/components/ScrollViewGesture.js +18 -13
- package/build/module/Carousel/components/ScrollViewGesture.js.map +1 -1
- package/build/module/Carousel/{hooks → components}/useItemInterpolation.js +3 -3
- package/build/module/Carousel/components/useItemInterpolation.js.map +1 -0
- package/build/module/Carousel/hooks/index.js +0 -2
- package/build/module/Carousel/hooks/index.js.map +1 -1
- package/build/module/Carousel/hooks/useAutoplayController.js +4 -1
- package/build/module/Carousel/hooks/useAutoplayController.js.map +1 -1
- package/build/module/Carousel/hooks/useIndexController.js +14 -39
- package/build/module/Carousel/hooks/useIndexController.js.map +1 -1
- package/build/module/Carousel/hooks/useItemVisibilityStore.js +10 -11
- package/build/module/Carousel/hooks/useItemVisibilityStore.js.map +1 -1
- package/build/module/Carousel/hooks/usePagingAnimation.js +122 -69
- package/build/module/Carousel/hooks/usePagingAnimation.js.map +1 -1
- package/build/module/Carousel/tick.js +6 -0
- package/build/module/Carousel/tick.js.map +1 -0
- package/build/module/Carousel/types.js.map +1 -1
- package/build/module/ViewPager/ChildrenMemoizedPage.js +53 -47
- package/build/module/ViewPager/ChildrenMemoizedPage.js.map +1 -1
- package/build/module/ViewPager/InternalContext.js +7 -0
- package/build/module/ViewPager/InternalContext.js.map +1 -0
- package/build/module/ViewPager/ViewPagerNative.js +72 -23
- package/build/module/ViewPager/ViewPagerNative.js.map +1 -1
- package/build/module/ViewPager/ViewPagerProps.js.map +1 -1
- package/build/module/ViewPager/ViewPagerWeb.js +21 -13
- package/build/module/ViewPager/ViewPagerWeb.js.map +1 -1
- package/build/module/ViewPager/index.js.map +1 -1
- package/build/module/ViewPager/types.js +2 -0
- package/build/module/ViewPager/types.js.map +1 -0
- package/build/module/ViewPager/usePageStore.js +25 -0
- package/build/module/ViewPager/usePageStore.js.map +1 -0
- package/build/module/ViewPager/utils.js.map +1 -1
- package/build/module/ViewabilityTrackerView/measureViewability.js +2 -2
- package/build/module/ViewabilityTrackerView/measureViewability.js.map +1 -1
- package/build/module/hooks/useUnstableCollapsibleAppBar.js +1 -1
- package/build/module/hooks/useUnstableCollapsibleAppBar.js.map +1 -1
- package/build/typescript/Carousel/CarouselProps.d.ts +4 -3
- package/build/typescript/Carousel/animation/parallaxItemStyleFactory.d.ts +5 -5
- package/build/typescript/Carousel/components/InternalContext.d.ts +2 -2
- package/build/typescript/Carousel/components/ItemView.d.ts +2 -0
- package/build/typescript/Carousel/components/RootView.d.ts +4 -4
- package/build/typescript/Carousel/components/ScrollViewGesture.d.ts +3 -3
- package/build/typescript/Carousel/{hooks → components}/useItemInterpolation.d.ts +0 -0
- package/build/typescript/Carousel/hooks/index.d.ts +0 -2
- package/build/typescript/Carousel/hooks/useIndexController.d.ts +0 -2
- package/build/typescript/Carousel/hooks/useItemVisibilityStore.d.ts +5 -2
- package/build/typescript/Carousel/hooks/usePagingAnimation.d.ts +8 -10
- package/build/typescript/Carousel/tick.d.ts +2 -0
- package/build/typescript/Carousel/types.d.ts +26 -5
- package/build/typescript/ViewPager/ChildrenMemoizedPage.d.ts +1 -1
- package/build/typescript/ViewPager/InternalContext.d.ts +7 -0
- package/build/typescript/ViewPager/ViewPagerNative.d.ts +2 -2
- package/build/typescript/ViewPager/ViewPagerProps.d.ts +4 -22
- package/build/typescript/ViewPager/ViewPagerWeb.d.ts +2 -2
- package/build/typescript/ViewPager/index.d.ts +2 -1
- package/build/typescript/ViewPager/types.d.ts +19 -0
- package/build/typescript/ViewPager/usePageStore.d.ts +2 -0
- package/build/typescript/ViewPager/utils.d.ts +1 -1
- package/package.json +3 -3
- package/src/Carousel/Carousel.tsx +32 -40
- package/src/Carousel/CarouselProps.ts +4 -3
- package/src/Carousel/animation/createDefaultScrollAnimation.ts +2 -2
- package/src/Carousel/animation/parallaxItemStyleFactory.ts +24 -24
- package/src/Carousel/components/InternalContext.ts +2 -2
- package/src/Carousel/components/ItemView.tsx +13 -3
- package/src/Carousel/components/RootView.tsx +19 -6
- package/src/Carousel/components/ScrollViewGesture.tsx +23 -15
- package/src/Carousel/{hooks → components}/useItemInterpolation.ts +3 -3
- package/src/Carousel/hooks/index.ts +0 -2
- package/src/Carousel/hooks/useAutoplayController.ts +4 -1
- package/src/Carousel/hooks/useIndexController.tsx +14 -44
- package/src/Carousel/hooks/useItemVisibilityStore.ts +17 -13
- package/src/Carousel/hooks/usePagingAnimation.ts +161 -83
- package/src/Carousel/tick.ts +6 -0
- package/src/Carousel/types.ts +34 -5
- package/src/ViewPager/ChildrenMemoizedPage.tsx +53 -50
- package/src/ViewPager/InternalContext.ts +13 -0
- package/src/ViewPager/ViewPagerNative.tsx +91 -44
- package/src/ViewPager/ViewPagerProps.ts +4 -27
- package/src/ViewPager/ViewPagerWeb.tsx +28 -23
- package/src/ViewPager/index.ts +2 -1
- package/src/ViewPager/types.ts +24 -0
- package/src/ViewPager/usePageStore.ts +30 -0
- package/src/ViewPager/utils.tsx +1 -1
- package/src/ViewabilityTrackerView/measureViewability.ts +1 -3
- package/src/hooks/useUnstableCollapsibleAppBar.ts +1 -1
- package/build/commonjs/Carousel/hooks/useDimensionChangeReaction.js +0 -23
- package/build/commonjs/Carousel/hooks/useDimensionChangeReaction.js.map +0 -1
- package/build/commonjs/Carousel/hooks/useItemInterpolation.js.map +0 -1
- package/build/module/Carousel/hooks/useDimensionChangeReaction.js +0 -14
- package/build/module/Carousel/hooks/useDimensionChangeReaction.js.map +0 -1
- package/build/module/Carousel/hooks/useItemInterpolation.js.map +0 -1
- package/build/typescript/Carousel/hooks/useDimensionChangeReaction.d.ts +0 -7
- package/src/Carousel/hooks/useDimensionChangeReaction.ts +0 -25
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { useCallback,
|
|
2
|
-
import { Animated
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
import { Animated } from 'react-native';
|
|
3
|
+
import type {
|
|
4
|
+
CreateScrollAnimation,
|
|
5
|
+
DirectionalPagingAnimationConfig,
|
|
6
|
+
IndexController,
|
|
7
|
+
IndexPagingAnimationConfig,
|
|
8
|
+
PagingAnimationConfig,
|
|
9
|
+
PagingAnimationType,
|
|
10
|
+
PagingDirection,
|
|
11
|
+
StartPagingAnimation,
|
|
12
|
+
} from '../types';
|
|
13
|
+
|
|
14
|
+
export interface PagingAnimationParameters {
|
|
7
15
|
createScrollAnimation: CreateScrollAnimation;
|
|
8
|
-
getCurrentIndex: GetCurrentIndex;
|
|
9
16
|
itemWidth: number;
|
|
10
|
-
|
|
17
|
+
indexController: IndexController;
|
|
11
18
|
loop: boolean;
|
|
12
19
|
numberOfData: number;
|
|
13
|
-
|
|
20
|
+
offsetX: Animated.Value;
|
|
21
|
+
translateX: Animated.Value;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
export interface UsePagingAnimation {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
startAnimation: StartPagingAnimation;
|
|
25
|
+
interruptAnimation: () => void;
|
|
26
|
+
startPagingAnimation: StartPagingAnimation;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
function directionToValue(itemWidth: number) {
|
|
@@ -32,105 +39,176 @@ function directionToValue(itemWidth: number) {
|
|
|
32
39
|
};
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
function toValueCompensator(itemWidth: number) {
|
|
43
|
+
return function (toValue: number, currentOffset: number): number {
|
|
44
|
+
const remainder = Math.abs(currentOffset % itemWidth);
|
|
45
|
+
|
|
46
|
+
const halfOfItemWidth = Math.abs(itemWidth / 2);
|
|
47
|
+
const compensateVector = remainder > halfOfItemWidth
|
|
48
|
+
? remainder - itemWidth
|
|
49
|
+
: remainder;
|
|
50
|
+
|
|
51
|
+
const direction = currentOffset > 0 ? -1 : 1;
|
|
52
|
+
|
|
53
|
+
return toValue + (direction * compensateVector);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default function usePagingAnimation(params: PagingAnimationParameters): UsePagingAnimation {
|
|
36
58
|
const {
|
|
37
|
-
controlledTx,
|
|
38
59
|
createScrollAnimation,
|
|
39
|
-
getCurrentIndex,
|
|
40
60
|
itemWidth,
|
|
41
|
-
|
|
61
|
+
indexController,
|
|
42
62
|
loop,
|
|
43
63
|
numberOfData,
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
offsetX,
|
|
65
|
+
translateX,
|
|
66
|
+
} = params;
|
|
46
67
|
|
|
47
|
-
const
|
|
48
|
-
|
|
68
|
+
const {
|
|
69
|
+
getCurrentIndex,
|
|
70
|
+
lastIndex,
|
|
71
|
+
notifyOffsetHasChanged,
|
|
72
|
+
} = indexController;
|
|
49
73
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
[controlledTx, offsetTx],
|
|
53
|
-
);
|
|
74
|
+
const toValueRef = useRef<number>(0);
|
|
75
|
+
const currentOffsetRef = useRef<number>(0);
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
const subscriptionId = controlledTx.addListener((value) => {
|
|
57
|
-
const currentTx = value.value;
|
|
77
|
+
const isAnimatingRef = useRef<boolean>(false);
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
if (currentTx !== 0) {
|
|
61
|
-
const maxWidth = numberOfData * itemWidth;
|
|
79
|
+
const maxWidth = Math.abs(numberOfData * itemWidth);
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
const ensureOffsetBoundary: (offset: number) => number = useCallback((offset: number) => {
|
|
82
|
+
if (loop) {
|
|
83
|
+
const isCloseToEnd = Math.abs(offset) >= (maxWidth - itemWidth);
|
|
84
|
+
if (isCloseToEnd) {
|
|
85
|
+
const signOfOffset = offset > 0 ? 1 : -1;
|
|
86
|
+
return offset + (-signOfOffset * maxWidth);
|
|
67
87
|
}
|
|
68
|
-
}
|
|
88
|
+
}
|
|
69
89
|
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
};
|
|
73
|
-
}, [numberOfData, itemWidth]);
|
|
90
|
+
return offset % maxWidth;
|
|
91
|
+
}, [itemWidth, loop, maxWidth]);
|
|
74
92
|
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
if (animationRef.current) {
|
|
78
|
-
animationRef.current?.stop();
|
|
79
|
-
animationRef.current = null;
|
|
80
|
-
}
|
|
81
|
-
};
|
|
93
|
+
const requireNewOffset = useCallback((newOffset: number) => {
|
|
94
|
+
const nextOffset = ensureOffsetBoundary(newOffset);
|
|
82
95
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
controlledTx.flattenOffset();
|
|
86
|
-
|
|
87
|
-
// FIXME: react-native-web bug maybe?
|
|
88
|
-
// `AnimatedValue.flattenOffset()` does not trigger any event listener.
|
|
89
|
-
// Accessing value directly via `_value` is dangerous but working on web (`useNativeDriver` always false).
|
|
90
|
-
// So setting same value with `value.setValue(value._value)` will trigger event listener.
|
|
91
|
-
if (Platform.OS === 'web') {
|
|
92
|
-
// @ts-ignore
|
|
93
|
-
controlledTx.setValue(controlledTx._value);
|
|
94
|
-
}
|
|
96
|
+
currentOffsetRef.current = nextOffset;
|
|
97
|
+
offsetX.setValue(nextOffset);
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
toValueRef.current = 0;
|
|
100
|
+
translateX.setValue(0);
|
|
101
|
+
}, [
|
|
102
|
+
ensureOffsetBoundary,
|
|
103
|
+
offsetX,
|
|
104
|
+
translateX,
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const interruptAnimation = useCallback(() => {
|
|
108
|
+
if (toValueRef.current === 0) {
|
|
109
|
+
// Performance optimization
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
translateX.stopAnimation(lastValue => {
|
|
114
|
+
isAnimatingRef.current = false;
|
|
99
115
|
|
|
100
|
-
|
|
116
|
+
const prevOffset = currentOffsetRef.current;
|
|
117
|
+
const totalOffset = prevOffset + lastValue;
|
|
101
118
|
|
|
102
|
-
|
|
103
|
-
}, [controlledTx]);
|
|
119
|
+
notifyOffsetHasChanged(totalOffset);
|
|
104
120
|
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
requireNewOffset(totalOffset);
|
|
122
|
+
});
|
|
123
|
+
}, [requireNewOffset, translateX]);
|
|
124
|
+
|
|
125
|
+
const finalizeAnimation = useCallback(() => {
|
|
126
|
+
isAnimatingRef.current = false;
|
|
127
|
+
|
|
128
|
+
const prevOffset = currentOffsetRef.current;
|
|
129
|
+
const toValue = toValueRef.current;
|
|
130
|
+
const totalOffset = prevOffset + toValue;
|
|
131
|
+
|
|
132
|
+
requireNewOffset(totalOffset);
|
|
133
|
+
}, [requireNewOffset]);
|
|
134
|
+
|
|
135
|
+
const startPagingAnimation = useCallback((type: PagingAnimationType, config: PagingAnimationConfig) => {
|
|
136
|
+
if (isAnimatingRef.current) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const configWithDefaults: PagingAnimationConfig = {
|
|
141
|
+
animated: true,
|
|
142
|
+
...config,
|
|
143
|
+
};
|
|
107
144
|
|
|
108
145
|
const currentIndex = getCurrentIndex();
|
|
109
146
|
|
|
110
|
-
const
|
|
147
|
+
const getValueByDirectionOnAllAdjacentItemsVisible = directionToValue(itemWidth);
|
|
148
|
+
const compensateToValue = toValueCompensator(itemWidth);
|
|
149
|
+
|
|
150
|
+
const getValueByDirectionalPagingOnLoopDisabled = (_config: DirectionalPagingAnimationConfig): number => {
|
|
151
|
+
const { direction, isOriginatedFromGesture } = _config;
|
|
152
|
+
|
|
111
153
|
if (currentIndex === 0 && direction === 'prev') {
|
|
112
|
-
return
|
|
113
|
-
?
|
|
154
|
+
return isOriginatedFromGesture
|
|
155
|
+
? getValueByDirectionOnAllAdjacentItemsVisible('stay')
|
|
114
156
|
: -lastIndex * itemWidth; // last position
|
|
115
157
|
} else if (currentIndex === lastIndex && direction === 'next') {
|
|
116
|
-
return
|
|
117
|
-
?
|
|
158
|
+
return isOriginatedFromGesture
|
|
159
|
+
? getValueByDirectionOnAllAdjacentItemsVisible('stay')
|
|
118
160
|
: lastIndex * itemWidth; // first position
|
|
119
161
|
}
|
|
120
|
-
return
|
|
162
|
+
return getValueByDirectionOnAllAdjacentItemsVisible(direction);
|
|
121
163
|
};
|
|
122
164
|
|
|
123
|
-
const
|
|
124
|
-
|
|
165
|
+
const getValueByDirectionalPaging = (_config: DirectionalPagingAnimationConfig): number => {
|
|
166
|
+
const _configWithDefaults: DirectionalPagingAnimationConfig = {
|
|
167
|
+
isOriginatedFromGesture: false,
|
|
168
|
+
..._config,
|
|
169
|
+
};
|
|
125
170
|
|
|
126
|
-
|
|
127
|
-
|
|
171
|
+
return loop
|
|
172
|
+
? getValueByDirectionOnAllAdjacentItemsVisible(_configWithDefaults.direction)
|
|
173
|
+
: getValueByDirectionalPagingOnLoopDisabled(_configWithDefaults);
|
|
174
|
+
};
|
|
128
175
|
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
176
|
+
const getValueByIndexPaging = ({ index }: IndexPagingAnimationConfig): number => {
|
|
177
|
+
if (index < 0 || index > lastIndex || index === currentIndex) {
|
|
178
|
+
// no animation if index is invalid or equals to current index
|
|
179
|
+
return 0;
|
|
132
180
|
}
|
|
133
|
-
|
|
181
|
+
|
|
182
|
+
const distance = Math.abs(currentIndex - index) * itemWidth;
|
|
183
|
+
const direction = index > currentIndex ? -1 : 1;
|
|
184
|
+
|
|
185
|
+
return distance * direction;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const wantedToValue = type === 'directional'
|
|
189
|
+
// @ts-ignore
|
|
190
|
+
? getValueByDirectionalPaging(configWithDefaults)
|
|
191
|
+
// @ts-ignore
|
|
192
|
+
: getValueByIndexPaging(configWithDefaults);
|
|
193
|
+
|
|
194
|
+
const toValue = compensateToValue(wantedToValue, currentOffsetRef.current);
|
|
195
|
+
|
|
196
|
+
toValueRef.current = toValue;
|
|
197
|
+
isAnimatingRef.current = true;
|
|
198
|
+
|
|
199
|
+
if (configWithDefaults.animated) {
|
|
200
|
+
const animation = createScrollAnimation(translateX, toValue);
|
|
201
|
+
|
|
202
|
+
animation.start(({ finished }) => {
|
|
203
|
+
if (finished) {
|
|
204
|
+
finalizeAnimation();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
finalizeAnimation();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
notifyOffsetHasChanged(currentOffsetRef.current + toValue);
|
|
134
212
|
}, [
|
|
135
213
|
createScrollAnimation,
|
|
136
214
|
getCurrentIndex,
|
|
@@ -138,11 +216,11 @@ export default function usePagingAnimation(config: PagingAnimationConfig): UsePa
|
|
|
138
216
|
itemWidth,
|
|
139
217
|
lastIndex,
|
|
140
218
|
loop,
|
|
219
|
+
notifyOffsetHasChanged,
|
|
141
220
|
]);
|
|
142
221
|
|
|
143
222
|
return {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
startAnimation,
|
|
223
|
+
interruptAnimation,
|
|
224
|
+
startPagingAnimation,
|
|
147
225
|
};
|
|
148
226
|
};
|
package/src/Carousel/types.ts
CHANGED
|
@@ -5,6 +5,8 @@ const directions = ['next', 'prev', 'stay'] as const;
|
|
|
5
5
|
|
|
6
6
|
export type PagingDirection = (typeof directions)[number];
|
|
7
7
|
|
|
8
|
+
export type ItemHeight = number | 'auto';
|
|
9
|
+
|
|
8
10
|
export interface RenderItem<T> {
|
|
9
11
|
(info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;
|
|
10
12
|
}
|
|
@@ -22,14 +24,30 @@ export interface GetCurrentIndex {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export interface IndexController {
|
|
25
|
-
currentIndex: number;
|
|
26
27
|
getCurrentIndex: GetCurrentIndex;
|
|
27
28
|
lastIndex: number;
|
|
28
|
-
|
|
29
|
+
notifyOffsetHasChanged: (offset: number) => void;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
export type PagingAnimationType = 'directional' | 'index';
|
|
33
|
+
|
|
34
|
+
export interface BasePagingAnimationConfig {
|
|
35
|
+
animated?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
39
|
+
direction: PagingDirection;
|
|
40
|
+
isOriginatedFromGesture?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
44
|
+
index: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;
|
|
48
|
+
|
|
31
49
|
export interface StartPagingAnimation {
|
|
32
|
-
(
|
|
50
|
+
(type: PagingAnimationType, config: PagingAnimationConfig): void;
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
export type VisibleIndexRanges = Array<[number, number]>;
|
|
@@ -49,7 +67,17 @@ export interface AutoplayController {
|
|
|
49
67
|
resume: () => void;
|
|
50
68
|
}
|
|
51
69
|
|
|
70
|
+
export interface ScrollToOption {
|
|
71
|
+
index: number;
|
|
72
|
+
animated?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
52
75
|
export interface CarouselInstance {
|
|
76
|
+
/**
|
|
77
|
+
* Get current visible item index.
|
|
78
|
+
*/
|
|
79
|
+
getCurrentIndex: GetCurrentIndex;
|
|
80
|
+
|
|
53
81
|
/**
|
|
54
82
|
* Scroll to next visible item.
|
|
55
83
|
*/
|
|
@@ -61,7 +89,8 @@ export interface CarouselInstance {
|
|
|
61
89
|
prev: () => void;
|
|
62
90
|
|
|
63
91
|
/**
|
|
64
|
-
*
|
|
92
|
+
* Scroll to desired indexed item.
|
|
93
|
+
* Invalid index is ignored.
|
|
65
94
|
*/
|
|
66
|
-
|
|
95
|
+
scrollTo: (option: ScrollToOption) => void;
|
|
67
96
|
}
|
|
@@ -1,92 +1,95 @@
|
|
|
1
|
-
import React, { memo, useMemo, useState } from 'react';
|
|
1
|
+
import React, { memo, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
2
2
|
import { Platform, View } from 'react-native';
|
|
3
|
-
import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
|
|
4
3
|
import { StyleSheet } from '@fountain-ui/core';
|
|
5
|
-
import type { PageProps } from './
|
|
4
|
+
import type { PageProps } from './types';
|
|
6
5
|
import PageStateContext from './PageStateContext';
|
|
6
|
+
import InternalContext from './InternalContext';
|
|
7
7
|
|
|
8
8
|
const styles = StyleSheet.create({
|
|
9
9
|
fill: { width: '100%', height: '100%' },
|
|
10
10
|
none: { display: 'none' },
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
interface
|
|
13
|
+
interface InternalPageDescription {
|
|
14
14
|
isActive: boolean;
|
|
15
|
-
|
|
15
|
+
becomeNeighbor: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface InternalPageState {
|
|
19
|
+
active: boolean;
|
|
20
|
+
loaded: boolean;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
function Page(props: PageProps) {
|
|
19
24
|
const {
|
|
20
25
|
index,
|
|
26
|
+
initialPage,
|
|
21
27
|
children,
|
|
22
28
|
loading,
|
|
23
29
|
offscreenPageRerenderLimit,
|
|
24
|
-
sharedIndex,
|
|
25
30
|
} = props;
|
|
26
31
|
|
|
27
|
-
const
|
|
28
|
-
const activeIndex = sharedIndex.value;
|
|
32
|
+
const { pageStore } = useContext(InternalContext);
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
const computePageDescription: (page: number) => InternalPageDescription = useCallback((page: number) => {
|
|
35
|
+
const activeIndex = page;
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
return { isActive, isLoaded: true };
|
|
34
|
-
}
|
|
37
|
+
const isActive = index === activeIndex;
|
|
35
38
|
|
|
36
|
-
const
|
|
39
|
+
const shouldRerender = index >= activeIndex - offscreenPageRerenderLimit
|
|
37
40
|
&& index <= activeIndex + offscreenPageRerenderLimit;
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
const becomeNeighbor = shouldRerender && !isActive;
|
|
43
|
+
|
|
44
|
+
return { isActive, becomeNeighbor };
|
|
45
|
+
}, [index]);
|
|
41
46
|
|
|
42
|
-
const [
|
|
47
|
+
const [initialState] = useState<InternalPageState>(() => {
|
|
48
|
+
const { isActive, becomeNeighbor } = computePageDescription(initialPage);
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
if (loading === 'eager') {
|
|
51
|
+
return { active: isActive, loaded: true };
|
|
52
|
+
}
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
...prevState,
|
|
49
|
-
isActive,
|
|
50
|
-
isLoaded: isActive || prevState.isLoaded,
|
|
51
|
-
}));
|
|
52
|
-
};
|
|
54
|
+
return { active: isActive, loaded: isActive || becomeNeighbor };
|
|
55
|
+
});
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
isLoaded: true,
|
|
58
|
-
}));
|
|
59
|
-
};
|
|
57
|
+
// `Bailing out of a state update` is not working as expected.
|
|
58
|
+
const pageStateRef = useRef<InternalPageState>(initialState);
|
|
59
|
+
const [, forceRender] = useReducer((s) => s + 1, 0);
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
() => {
|
|
63
|
-
const activeIndex = sharedIndex.value;
|
|
61
|
+
const { active, loaded } = pageStateRef.current;
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
const content = loaded ? children : null;
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
return pageStore.subscribe(newPage => {
|
|
67
|
+
const { isActive, becomeNeighbor } = computePageDescription(newPage);
|
|
69
68
|
|
|
70
|
-
const
|
|
69
|
+
const currentState = pageStateRef.current;
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
const newState: InternalPageState = {
|
|
72
|
+
active: isActive,
|
|
73
|
+
loaded: isActive || becomeNeighbor ? true : currentState.loaded,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
currentState.active !== newState.active
|
|
78
|
+
|| currentState.loaded !== newState.loaded
|
|
79
|
+
) {
|
|
80
|
+
pageStateRef.current = newState;
|
|
81
|
+
|
|
82
|
+
forceRender();
|
|
79
83
|
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
);
|
|
84
|
+
});
|
|
85
|
+
}, [pageStore, computePageDescription]);
|
|
83
86
|
|
|
84
87
|
const contextValue = useMemo(() => ({
|
|
85
|
-
isActive:
|
|
86
|
-
}), [
|
|
88
|
+
isActive: active,
|
|
89
|
+
}), [active]);
|
|
87
90
|
|
|
88
91
|
const style = Platform.OS === 'web'
|
|
89
|
-
? (
|
|
92
|
+
? (active ? StyleSheet.absoluteFill : styles.none)
|
|
90
93
|
: styles.fill;
|
|
91
94
|
|
|
92
95
|
return (
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
import type { MonoStore } from '@fountain-ui/core';
|
|
3
|
+
import { MockStore } from '@fountain-ui/core';
|
|
4
|
+
|
|
5
|
+
export interface InternalContextValue {
|
|
6
|
+
pageStore: MonoStore<number>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const InternalContext = createContext<InternalContextValue>({
|
|
10
|
+
pageStore: new MockStore(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export default InternalContext;
|