@fountain-ui/lab 2.0.0-beta.12 → 2.0.0-beta.13
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 +12 -7
- package/build/commonjs/Carousel/Carousel.js.map +1 -1
- package/build/commonjs/Carousel/CarouselProps.js.map +1 -1
- package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js +13 -13
- 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 +4 -2
- 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 +12 -7
- package/build/commonjs/Carousel/components/ScrollViewGesture.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/usePagingAnimation.js +57 -19
- package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +1 -1
- package/build/commonjs/Carousel/types.js.map +1 -1
- package/build/commonjs/ViewPager/ViewPagerNative.js +45 -17
- package/build/commonjs/ViewPager/ViewPagerNative.js.map +1 -1
- package/build/commonjs/ViewPager/ViewPagerProps.js.map +1 -1
- package/build/commonjs/ViewPager/ViewPagerWeb.js +7 -7
- package/build/commonjs/ViewPager/ViewPagerWeb.js.map +1 -1
- package/build/module/Carousel/Carousel.js +12 -7
- package/build/module/Carousel/Carousel.js.map +1 -1
- package/build/module/Carousel/CarouselProps.js.map +1 -1
- package/build/module/Carousel/animation/parallaxItemStyleFactory.js +13 -13
- 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 +4 -2
- 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 +12 -7
- package/build/module/Carousel/components/ScrollViewGesture.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/usePagingAnimation.js +57 -19
- package/build/module/Carousel/hooks/usePagingAnimation.js.map +1 -1
- package/build/module/Carousel/types.js.map +1 -1
- package/build/module/ViewPager/ViewPagerNative.js +45 -17
- package/build/module/ViewPager/ViewPagerNative.js.map +1 -1
- package/build/module/ViewPager/ViewPagerProps.js.map +1 -1
- package/build/module/ViewPager/ViewPagerWeb.js +8 -8
- package/build/module/ViewPager/ViewPagerWeb.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 +1 -1
- package/build/typescript/Carousel/hooks/usePagingAnimation.d.ts +3 -3
- package/build/typescript/Carousel/types.d.ts +25 -3
- package/build/typescript/ViewPager/ViewPagerNative.d.ts +1 -1
- package/build/typescript/ViewPager/ViewPagerProps.d.ts +1 -1
- package/build/typescript/ViewPager/ViewPagerWeb.d.ts +1 -1
- package/package.json +2 -2
- package/src/Carousel/Carousel.tsx +7 -6
- package/src/Carousel/CarouselProps.ts +4 -3
- package/src/Carousel/animation/parallaxItemStyleFactory.ts +23 -23
- package/src/Carousel/components/InternalContext.ts +2 -2
- package/src/Carousel/components/ItemView.tsx +12 -2
- package/src/Carousel/components/RootView.tsx +19 -6
- package/src/Carousel/components/ScrollViewGesture.tsx +15 -8
- package/src/Carousel/hooks/useAutoplayController.ts +4 -1
- package/src/Carousel/hooks/usePagingAnimation.ts +70 -23
- package/src/Carousel/types.ts +33 -3
- package/src/ViewPager/ViewPagerNative.tsx +49 -16
- package/src/ViewPager/ViewPagerProps.ts +1 -1
- package/src/ViewPager/ViewPagerWeb.tsx +8 -8
|
@@ -2,6 +2,7 @@ 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
|
+
export declare type ItemHeight = number | 'auto';
|
|
5
6
|
export interface RenderItem<T> {
|
|
6
7
|
(info: {
|
|
7
8
|
item: T;
|
|
@@ -24,8 +25,20 @@ export interface IndexController {
|
|
|
24
25
|
lastIndex: number;
|
|
25
26
|
monitorElement: ReactElement;
|
|
26
27
|
}
|
|
28
|
+
export declare type PagingAnimationType = 'directional' | 'index';
|
|
29
|
+
export interface BasePagingAnimationConfig {
|
|
30
|
+
animated?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
33
|
+
direction: PagingDirection;
|
|
34
|
+
isOriginatedFromGesture?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
37
|
+
index: number;
|
|
38
|
+
}
|
|
39
|
+
export declare type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;
|
|
27
40
|
export interface StartPagingAnimation {
|
|
28
|
-
(
|
|
41
|
+
(type: PagingAnimationType, config: PagingAnimationConfig): void;
|
|
29
42
|
}
|
|
30
43
|
export declare type VisibleIndexRanges = Array<[number, number]>;
|
|
31
44
|
export interface StoreSubscription {
|
|
@@ -40,7 +53,15 @@ export interface AutoplayController {
|
|
|
40
53
|
pause: () => void;
|
|
41
54
|
resume: () => void;
|
|
42
55
|
}
|
|
56
|
+
export interface ScrollToOption {
|
|
57
|
+
index: number;
|
|
58
|
+
animated?: boolean;
|
|
59
|
+
}
|
|
43
60
|
export interface CarouselInstance {
|
|
61
|
+
/**
|
|
62
|
+
* Get current visible item index.
|
|
63
|
+
*/
|
|
64
|
+
getCurrentIndex: GetCurrentIndex;
|
|
44
65
|
/**
|
|
45
66
|
* Scroll to next visible item.
|
|
46
67
|
*/
|
|
@@ -50,8 +71,9 @@ export interface CarouselInstance {
|
|
|
50
71
|
*/
|
|
51
72
|
prev: () => void;
|
|
52
73
|
/**
|
|
53
|
-
*
|
|
74
|
+
* Scroll to desired indexed item.
|
|
75
|
+
* Invalid index is ignored.
|
|
54
76
|
*/
|
|
55
|
-
|
|
77
|
+
scrollTo: (option: ScrollToOption) => void;
|
|
56
78
|
}
|
|
57
79
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type ViewPagerProps from './ViewPagerProps';
|
|
3
3
|
import type { ViewPagerInstance } from './ViewPagerProps';
|
|
4
|
-
declare const ViewPager: React.ForwardRefExoticComponent<Pick<ViewPagerProps, "style" | "children" | "onChange" | "scrollEnabled" | "keyboardDismissMode" | "loading" | "initialPage" | "offscreenPageRerenderLimit" | "pageComponent" | "pageForceRerenderKey" | "
|
|
4
|
+
declare const ViewPager: React.ForwardRefExoticComponent<Pick<ViewPagerProps, "style" | "children" | "onChange" | "scrollEnabled" | "keyboardDismissMode" | "loading" | "initialPage" | "offscreenPageRerenderLimit" | "pageComponent" | "pageForceRerenderKey" | "UNSTABLE_sharedPage"> & React.RefAttributes<ViewPagerInstance>>;
|
|
5
5
|
export default ViewPager;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type ViewPagerProps from './ViewPagerProps';
|
|
3
3
|
import type { ViewPagerInstance } from './ViewPagerProps';
|
|
4
|
-
declare const ViewPager: React.ForwardRefExoticComponent<Pick<ViewPagerProps, "style" | "children" | "onChange" | "scrollEnabled" | "keyboardDismissMode" | "loading" | "initialPage" | "offscreenPageRerenderLimit" | "pageComponent" | "pageForceRerenderKey" | "
|
|
4
|
+
declare const ViewPager: React.ForwardRefExoticComponent<Pick<ViewPagerProps, "style" | "children" | "onChange" | "scrollEnabled" | "keyboardDismissMode" | "loading" | "initialPage" | "offscreenPageRerenderLimit" | "pageComponent" | "pageForceRerenderKey" | "UNSTABLE_sharedPage"> & React.RefAttributes<ViewPagerInstance>>;
|
|
5
5
|
export default ViewPager;
|
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.13",
|
|
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": "4f247070bf942db98feafb9573e8c1dc3fc1d758"
|
|
74
74
|
}
|
|
@@ -61,7 +61,7 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
61
61
|
const {
|
|
62
62
|
finalizeAnimation,
|
|
63
63
|
globalInterpolation,
|
|
64
|
-
|
|
64
|
+
startPagingAnimation,
|
|
65
65
|
} = usePagingAnimation({
|
|
66
66
|
controlledTx,
|
|
67
67
|
createScrollAnimation,
|
|
@@ -76,7 +76,7 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
76
76
|
const autoplayController = useAutoplayController({
|
|
77
77
|
enabled: autoplay,
|
|
78
78
|
intervalMillis: autoplayInterval,
|
|
79
|
-
startPagingAnimation
|
|
79
|
+
startPagingAnimation,
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
useDimensionChangeReaction({
|
|
@@ -88,11 +88,12 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
88
88
|
useImperativeHandle(
|
|
89
89
|
ref,
|
|
90
90
|
() => ({
|
|
91
|
-
next: () => startAnimation('next'),
|
|
92
|
-
prev: () => startAnimation('prev'),
|
|
93
91
|
getCurrentIndex,
|
|
92
|
+
next: () => startPagingAnimation('directional', { direction: 'next' }),
|
|
93
|
+
prev: () => startPagingAnimation('directional', { direction: 'prev' }),
|
|
94
|
+
scrollTo: (option) => startPagingAnimation('index', option),
|
|
94
95
|
}),
|
|
95
|
-
[
|
|
96
|
+
[startPagingAnimation, getCurrentIndex],
|
|
96
97
|
);
|
|
97
98
|
|
|
98
99
|
const contextValue = useMemo(() => ({
|
|
@@ -133,7 +134,7 @@ const Carousel = forwardRef<CarouselInstance, CarouselProps>(function Carousel(p
|
|
|
133
134
|
finalizeAnimation={finalizeAnimation}
|
|
134
135
|
offsetTx={offsetTx}
|
|
135
136
|
scrollEnabled={scrollEnabled}
|
|
136
|
-
|
|
137
|
+
startPagingAnimation={startPagingAnimation}
|
|
137
138
|
>
|
|
138
139
|
<RootView
|
|
139
140
|
data={data}
|
|
@@ -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, RenderItem } from './types';
|
|
3
|
+
import type { CarouselInstance, CreateItemStyle, CreateScrollAnimation, ItemHeight, RenderItem } from './types';
|
|
4
4
|
|
|
5
5
|
export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
6
6
|
ref?: RefObject<CarouselInstance>;
|
|
@@ -47,9 +47,10 @@ export default interface CarouselProps<ItemT = any> extends ComponentProps<{
|
|
|
47
47
|
initialIndex?: number;
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* The item
|
|
50
|
+
* The item height.
|
|
51
|
+
* For a performance reason, always consider to provide a number value.
|
|
51
52
|
*/
|
|
52
|
-
itemHeight:
|
|
53
|
+
itemHeight: ItemHeight;
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* The item width.
|
|
@@ -2,28 +2,28 @@ import type { CreateItemStyle } from '../types';
|
|
|
2
2
|
import { itemInterpolationInputRange } from './animationUtils';
|
|
3
3
|
|
|
4
4
|
export interface ParallaxAnimationConfig {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
activeItemScale?: number;
|
|
6
|
+
activeItemOpacity?: number;
|
|
7
|
+
adjacentItemScale?: number;
|
|
8
|
+
adjacentItemOpacity?: number;
|
|
9
|
+
scrollingOffset?: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const defaultParallaxAnimationConfig: Required<Readonly<ParallaxAnimationConfig>> = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
activeItemScale: 0.9,
|
|
14
|
+
activeItemOpacity: 1,
|
|
15
|
+
adjacentItemScale: Math.pow(0.9, 2),
|
|
16
|
+
adjacentItemOpacity: 0.5,
|
|
17
|
+
scrollingOffset: 50,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export default function parallaxItemStyleFactory(config: ParallaxAnimationConfig = defaultParallaxAnimationConfig) {
|
|
21
21
|
const {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
activeItemOpacity,
|
|
23
|
+
activeItemScale,
|
|
24
|
+
adjacentItemOpacity,
|
|
25
|
+
adjacentItemScale,
|
|
26
|
+
scrollingOffset,
|
|
27
27
|
}: Required<ParallaxAnimationConfig> = {
|
|
28
28
|
...config,
|
|
29
29
|
...defaultParallaxAnimationConfig,
|
|
@@ -33,9 +33,9 @@ export default function parallaxItemStyleFactory(config: ParallaxAnimationConfig
|
|
|
33
33
|
const translate = itemInterpolation.interpolate({
|
|
34
34
|
inputRange: itemInterpolationInputRange,
|
|
35
35
|
outputRange: [
|
|
36
|
-
-itemWidth +
|
|
36
|
+
-itemWidth + scrollingOffset,
|
|
37
37
|
0,
|
|
38
|
-
itemWidth -
|
|
38
|
+
itemWidth - scrollingOffset,
|
|
39
39
|
],
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -48,9 +48,9 @@ export default function parallaxItemStyleFactory(config: ParallaxAnimationConfig
|
|
|
48
48
|
const scale = itemInterpolation.interpolate({
|
|
49
49
|
inputRange: itemInterpolationInputRange,
|
|
50
50
|
outputRange: [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
adjacentItemScale,
|
|
52
|
+
activeItemScale,
|
|
53
|
+
adjacentItemScale,
|
|
54
54
|
],
|
|
55
55
|
extrapolate: 'clamp',
|
|
56
56
|
});
|
|
@@ -58,9 +58,9 @@ export default function parallaxItemStyleFactory(config: ParallaxAnimationConfig
|
|
|
58
58
|
const opacity = itemInterpolation.interpolate({
|
|
59
59
|
inputRange: itemInterpolationInputRange,
|
|
60
60
|
outputRange: [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
adjacentItemOpacity,
|
|
62
|
+
activeItemOpacity,
|
|
63
|
+
adjacentItemOpacity,
|
|
64
64
|
],
|
|
65
65
|
extrapolate: 'clamp',
|
|
66
66
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
import { Animated } from 'react-native';
|
|
3
|
-
import type { CreateItemStyle, ItemVisibilityStore } from '../types';
|
|
3
|
+
import type { CreateItemStyle, ItemHeight, ItemVisibilityStore } from '../types';
|
|
4
4
|
|
|
5
5
|
export interface InternalContextValue<ItemT> {
|
|
6
6
|
createItemStyle: CreateItemStyle;
|
|
7
7
|
data: ReadonlyArray<ItemT>;
|
|
8
8
|
globalInterpolation: Animated.AnimatedInterpolation;
|
|
9
|
-
itemHeight:
|
|
9
|
+
itemHeight: ItemHeight;
|
|
10
10
|
itemWidth: number;
|
|
11
11
|
itemVisibilityStore: ItemVisibilityStore;
|
|
12
12
|
loop: boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import type { ViewProps } from 'react-native';
|
|
3
4
|
import { Animated } from 'react-native';
|
|
4
5
|
import { StyleSheet } from '@fountain-ui/core';
|
|
5
6
|
import { useItemInterpolation } from '../hooks';
|
|
@@ -8,10 +9,15 @@ import InternalContext from './InternalContext';
|
|
|
8
9
|
export interface ItemViewProps {
|
|
9
10
|
children: (interpolation: Animated.AnimatedInterpolation) => ReactElement | null;
|
|
10
11
|
index: number;
|
|
12
|
+
onLayout?: ViewProps['onLayout'];
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export default function ItemView(props: ItemViewProps) {
|
|
14
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
children,
|
|
18
|
+
index,
|
|
19
|
+
onLayout,
|
|
20
|
+
} = props;
|
|
15
21
|
|
|
16
22
|
const {
|
|
17
23
|
createItemStyle,
|
|
@@ -39,8 +45,12 @@ export default function ItemView(props: ItemViewProps) {
|
|
|
39
45
|
return (
|
|
40
46
|
<Animated.View
|
|
41
47
|
children={visible ? children(interpolation) : null}
|
|
48
|
+
onLayout={onLayout}
|
|
42
49
|
style={[
|
|
43
|
-
{
|
|
50
|
+
{
|
|
51
|
+
width: itemWidth,
|
|
52
|
+
height: itemHeight !== 'auto' ? itemHeight : undefined,
|
|
53
|
+
},
|
|
44
54
|
styles.absolute,
|
|
45
55
|
// @ts-ignore
|
|
46
56
|
itemStyle,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { memo, ReactElement,
|
|
2
|
-
import type { ViewProps,
|
|
1
|
+
import React, { forwardRef, memo, ReactElement, useState } from 'react';
|
|
2
|
+
import type { View, ViewProps, LayoutChangeEvent } from 'react-native';
|
|
3
3
|
import { Animated } from 'react-native';
|
|
4
4
|
import { StyleSheet } from '@fountain-ui/core';
|
|
5
|
-
import type { RenderItem } from '../types';
|
|
5
|
+
import type { ItemHeight, RenderItem } from '../types';
|
|
6
6
|
import ItemView from './ItemView';
|
|
7
7
|
|
|
8
8
|
const styles = StyleSheet.create({
|
|
@@ -12,13 +12,14 @@ const styles = StyleSheet.create({
|
|
|
12
12
|
},
|
|
13
13
|
horizontal: {
|
|
14
14
|
flexDirection: 'row',
|
|
15
|
-
|
|
15
|
+
overflowX: 'hidden',
|
|
16
|
+
overflowY: 'visible',
|
|
16
17
|
},
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
export type RootViewProps<ItemT> = ViewProps & {
|
|
20
21
|
data: ReadonlyArray<ItemT>;
|
|
21
|
-
itemHeight:
|
|
22
|
+
itemHeight: ItemHeight;
|
|
22
23
|
originalData: ReadonlyArray<ItemT>;
|
|
23
24
|
renderItem: RenderItem<ItemT>;
|
|
24
25
|
};
|
|
@@ -33,10 +34,22 @@ const RootView = forwardRef<View, RootViewProps<any>>(function RootView(props, r
|
|
|
33
34
|
...otherProps
|
|
34
35
|
} = props;
|
|
35
36
|
|
|
37
|
+
const [maxHeightOfRenderedItems, setMaxHeightOfRenderedItems] = useState(0);
|
|
38
|
+
|
|
39
|
+
const handleItemLayout = (e: LayoutChangeEvent) => {
|
|
40
|
+
const { nativeEvent: { layout: { height } } } = e;
|
|
41
|
+
|
|
42
|
+
const heightInt = Math.round(height);
|
|
43
|
+
if (heightInt > maxHeightOfRenderedItems) {
|
|
44
|
+
setMaxHeightOfRenderedItems(heightInt);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
36
48
|
const renderItemView = (item: any, index: number): ReactElement => (
|
|
37
49
|
<ItemView
|
|
38
50
|
key={index}
|
|
39
51
|
index={index}
|
|
52
|
+
onLayout={itemHeight === 'auto' ? handleItemLayout : undefined}
|
|
40
53
|
>
|
|
41
54
|
{(interpolation) => renderItem({
|
|
42
55
|
item,
|
|
@@ -49,7 +62,7 @@ const RootView = forwardRef<View, RootViewProps<any>>(function RootView(props, r
|
|
|
49
62
|
const viewStyles = [
|
|
50
63
|
styles.root,
|
|
51
64
|
styles.horizontal,
|
|
52
|
-
{ height: itemHeight },
|
|
65
|
+
{ height: itemHeight === 'auto' ? maxHeightOfRenderedItems : itemHeight },
|
|
53
66
|
style,
|
|
54
67
|
];
|
|
55
68
|
|
|
@@ -11,11 +11,11 @@ export interface ScrollViewGestureProps {
|
|
|
11
11
|
finalizeAnimation: () => void;
|
|
12
12
|
offsetTx: Animated.Value,
|
|
13
13
|
scrollEnabled: boolean;
|
|
14
|
-
|
|
14
|
+
startPagingAnimation: StartPagingAnimation;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const SCROLL_TO_ADJACENT_TX_THRESHOLD =
|
|
18
|
-
const SCROLL_TO_ADJACENT_VX_THRESHOLD =
|
|
17
|
+
const SCROLL_TO_ADJACENT_TX_THRESHOLD = 80;
|
|
18
|
+
const SCROLL_TO_ADJACENT_VX_THRESHOLD = 1000;
|
|
19
19
|
|
|
20
20
|
const ACTIVE_OFFSET_ABS_X = 5;
|
|
21
21
|
const activeOffsetX: number[] = [-ACTIVE_OFFSET_ABS_X, ACTIVE_OFFSET_ABS_X];
|
|
@@ -26,8 +26,12 @@ const endAnimationStates: Readonly<GestureHandlerState[]> = [
|
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
function shouldScrollToAdjacent(translationX: number, velocityX: number): boolean {
|
|
29
|
-
|
|
29
|
+
const isSameDirection = translationX * velocityX > 0;
|
|
30
|
+
const isEnoughMovement =
|
|
31
|
+
Math.abs(translationX) >= SCROLL_TO_ADJACENT_TX_THRESHOLD
|
|
30
32
|
|| Math.abs(velocityX) >= SCROLL_TO_ADJACENT_VX_THRESHOLD;
|
|
33
|
+
|
|
34
|
+
return isSameDirection && isEnoughMovement;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
@@ -37,7 +41,7 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
|
37
41
|
finalizeAnimation,
|
|
38
42
|
offsetTx,
|
|
39
43
|
scrollEnabled,
|
|
40
|
-
|
|
44
|
+
startPagingAnimation,
|
|
41
45
|
} = props;
|
|
42
46
|
|
|
43
47
|
const { pause: pauseAutoplay, resume: resumeAutoplay } = autoplayController;
|
|
@@ -57,15 +61,18 @@ export default function ScrollViewGesture(props: ScrollViewGestureProps) {
|
|
|
57
61
|
const { nativeEvent: { translationX, velocityX, state } } = event;
|
|
58
62
|
|
|
59
63
|
if (endAnimationStates.includes(state)) {
|
|
60
|
-
const
|
|
64
|
+
const direction: PagingDirection = shouldScrollToAdjacent(translationX, velocityX)
|
|
61
65
|
? (translationX < 0 ? 'next' : 'prev')
|
|
62
66
|
: 'stay';
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
startPagingAnimation(
|
|
69
|
+
'directional',
|
|
70
|
+
{ direction: direction, isOriginatedFromGesture: true },
|
|
71
|
+
);
|
|
65
72
|
|
|
66
73
|
resumeAutoplay();
|
|
67
74
|
}
|
|
68
|
-
}, [
|
|
75
|
+
}, [startPagingAnimation, resumeAutoplay]);
|
|
69
76
|
|
|
70
77
|
return (
|
|
71
78
|
<PanGestureHandler
|
|
@@ -25,7 +25,10 @@ export default function useAutoplayController(config: AutoplayConfig): AutoplayC
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
executor.execute(() => {
|
|
28
|
-
startPagingAnimation(
|
|
28
|
+
startPagingAnimation(
|
|
29
|
+
'directional',
|
|
30
|
+
{ direction: 'next', isOriginatedFromGesture: false },
|
|
31
|
+
);
|
|
29
32
|
play();
|
|
30
33
|
}, intervalMillis);
|
|
31
34
|
}, [intervalMillis, startPagingAnimation]);
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { Animated, Platform } from 'react-native';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import type {
|
|
4
|
+
CreateScrollAnimation,
|
|
5
|
+
DirectionalPagingAnimationConfig,
|
|
6
|
+
GetCurrentIndex,
|
|
7
|
+
IndexPagingAnimationConfig,
|
|
8
|
+
PagingAnimationConfig,
|
|
9
|
+
PagingAnimationType,
|
|
10
|
+
PagingDirection,
|
|
11
|
+
StartPagingAnimation,
|
|
12
|
+
} from '../types';
|
|
13
|
+
|
|
14
|
+
export interface PagingAnimationParameters {
|
|
6
15
|
controlledTx: Animated.Value;
|
|
7
16
|
createScrollAnimation: CreateScrollAnimation;
|
|
8
17
|
getCurrentIndex: GetCurrentIndex;
|
|
@@ -16,7 +25,7 @@ export interface PagingAnimationConfig {
|
|
|
16
25
|
export interface UsePagingAnimation {
|
|
17
26
|
finalizeAnimation: () => void;
|
|
18
27
|
globalInterpolation: Animated.AnimatedInterpolation;
|
|
19
|
-
|
|
28
|
+
startPagingAnimation: StartPagingAnimation;
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
function directionToValue(itemWidth: number) {
|
|
@@ -32,7 +41,7 @@ function directionToValue(itemWidth: number) {
|
|
|
32
41
|
};
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
export default function usePagingAnimation(
|
|
44
|
+
export default function usePagingAnimation(params: PagingAnimationParameters): UsePagingAnimation {
|
|
36
45
|
const {
|
|
37
46
|
controlledTx,
|
|
38
47
|
createScrollAnimation,
|
|
@@ -42,7 +51,7 @@ export default function usePagingAnimation(config: PagingAnimationConfig): UsePa
|
|
|
42
51
|
loop,
|
|
43
52
|
numberOfData,
|
|
44
53
|
offsetTx,
|
|
45
|
-
} =
|
|
54
|
+
} = params;
|
|
46
55
|
|
|
47
56
|
const animationRef = useRef<Animated.CompositeAnimation | null>(null);
|
|
48
57
|
const toValueRef = useRef<number>(0);
|
|
@@ -102,35 +111,73 @@ export default function usePagingAnimation(config: PagingAnimationConfig): UsePa
|
|
|
102
111
|
resetBoundary();
|
|
103
112
|
}, [controlledTx]);
|
|
104
113
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
114
|
+
const startPagingAnimation = useCallback((type: PagingAnimationType, config: PagingAnimationConfig) => {
|
|
115
|
+
const configWithDefaults: PagingAnimationConfig = {
|
|
116
|
+
animated: true,
|
|
117
|
+
...config,
|
|
118
|
+
};
|
|
107
119
|
|
|
108
120
|
const currentIndex = getCurrentIndex();
|
|
109
121
|
|
|
110
|
-
const
|
|
122
|
+
const getValueByDirectionOnAllAdjacentItemsVisible = directionToValue(itemWidth);
|
|
123
|
+
|
|
124
|
+
const getValueByDirectionalPagingOnLoopDisabled = (_config: DirectionalPagingAnimationConfig): number => {
|
|
125
|
+
const { direction, isOriginatedFromGesture } = _config;
|
|
126
|
+
|
|
111
127
|
if (currentIndex === 0 && direction === 'prev') {
|
|
112
|
-
return
|
|
113
|
-
?
|
|
128
|
+
return isOriginatedFromGesture
|
|
129
|
+
? getValueByDirectionOnAllAdjacentItemsVisible('stay')
|
|
114
130
|
: -lastIndex * itemWidth; // last position
|
|
115
131
|
} else if (currentIndex === lastIndex && direction === 'next') {
|
|
116
|
-
return
|
|
117
|
-
?
|
|
132
|
+
return isOriginatedFromGesture
|
|
133
|
+
? getValueByDirectionOnAllAdjacentItemsVisible('stay')
|
|
118
134
|
: lastIndex * itemWidth; // first position
|
|
119
135
|
}
|
|
120
|
-
return
|
|
136
|
+
return getValueByDirectionOnAllAdjacentItemsVisible(direction);
|
|
121
137
|
};
|
|
122
138
|
|
|
123
|
-
const
|
|
124
|
-
|
|
139
|
+
const getValueByDirectionalPaging = (_config: DirectionalPagingAnimationConfig): number => {
|
|
140
|
+
const _configWithDefaults: DirectionalPagingAnimationConfig = {
|
|
141
|
+
isOriginatedFromGesture: false,
|
|
142
|
+
..._config,
|
|
143
|
+
};
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
return loop
|
|
146
|
+
? getValueByDirectionOnAllAdjacentItemsVisible(_configWithDefaults.direction)
|
|
147
|
+
: getValueByDirectionalPagingOnLoopDisabled(_configWithDefaults);
|
|
148
|
+
};
|
|
128
149
|
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
150
|
+
const getValueByIndexPaging = ({ index }: IndexPagingAnimationConfig): number => {
|
|
151
|
+
if (index < 0 || index > lastIndex || index === currentIndex) {
|
|
152
|
+
// no animation if index is invalid or equals to current index
|
|
153
|
+
return 0;
|
|
132
154
|
}
|
|
133
|
-
|
|
155
|
+
|
|
156
|
+
const distance = Math.abs(currentIndex - index) * itemWidth;
|
|
157
|
+
const direction = index > currentIndex ? -1 : 1;
|
|
158
|
+
|
|
159
|
+
return distance * direction;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const toValue = type === 'directional'
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
? getValueByDirectionalPaging(configWithDefaults)
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
: getValueByIndexPaging(configWithDefaults);
|
|
167
|
+
|
|
168
|
+
toValueRef.current = toValue;
|
|
169
|
+
|
|
170
|
+
if (configWithDefaults.animated) {
|
|
171
|
+
const animation = createScrollAnimation(offsetTx, toValue);
|
|
172
|
+
animationRef.current = animation;
|
|
173
|
+
animation.start(({ finished }) => {
|
|
174
|
+
if (finished) {
|
|
175
|
+
finalizeAnimation();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
finalizeAnimation();
|
|
180
|
+
}
|
|
134
181
|
}, [
|
|
135
182
|
createScrollAnimation,
|
|
136
183
|
getCurrentIndex,
|
|
@@ -143,6 +190,6 @@ export default function usePagingAnimation(config: PagingAnimationConfig): UsePa
|
|
|
143
190
|
return {
|
|
144
191
|
globalInterpolation,
|
|
145
192
|
finalizeAnimation,
|
|
146
|
-
|
|
193
|
+
startPagingAnimation,
|
|
147
194
|
};
|
|
148
195
|
};
|
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
|
}
|
|
@@ -28,8 +30,25 @@ export interface IndexController {
|
|
|
28
30
|
monitorElement: ReactElement;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
export type PagingAnimationType = 'directional' | 'index';
|
|
34
|
+
|
|
35
|
+
export interface BasePagingAnimationConfig {
|
|
36
|
+
animated?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
40
|
+
direction: PagingDirection;
|
|
41
|
+
isOriginatedFromGesture?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {
|
|
45
|
+
index: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;
|
|
49
|
+
|
|
31
50
|
export interface StartPagingAnimation {
|
|
32
|
-
(
|
|
51
|
+
(type: PagingAnimationType, config: PagingAnimationConfig): void;
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
export type VisibleIndexRanges = Array<[number, number]>;
|
|
@@ -49,7 +68,17 @@ export interface AutoplayController {
|
|
|
49
68
|
resume: () => void;
|
|
50
69
|
}
|
|
51
70
|
|
|
71
|
+
export interface ScrollToOption {
|
|
72
|
+
index: number;
|
|
73
|
+
animated?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
52
76
|
export interface CarouselInstance {
|
|
77
|
+
/**
|
|
78
|
+
* Get current visible item index.
|
|
79
|
+
*/
|
|
80
|
+
getCurrentIndex: GetCurrentIndex;
|
|
81
|
+
|
|
53
82
|
/**
|
|
54
83
|
* Scroll to next visible item.
|
|
55
84
|
*/
|
|
@@ -61,7 +90,8 @@ export interface CarouselInstance {
|
|
|
61
90
|
prev: () => void;
|
|
62
91
|
|
|
63
92
|
/**
|
|
64
|
-
*
|
|
93
|
+
* Scroll to desired indexed item.
|
|
94
|
+
* Invalid index is ignored.
|
|
65
95
|
*/
|
|
66
|
-
|
|
96
|
+
scrollTo: (option: ScrollToOption) => void;
|
|
67
97
|
}
|