@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.
Files changed (69) hide show
  1. package/build/commonjs/Carousel/Carousel.js +12 -7
  2. package/build/commonjs/Carousel/Carousel.js.map +1 -1
  3. package/build/commonjs/Carousel/CarouselProps.js.map +1 -1
  4. package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js +13 -13
  5. package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js.map +1 -1
  6. package/build/commonjs/Carousel/components/InternalContext.js.map +1 -1
  7. package/build/commonjs/Carousel/components/ItemView.js +4 -2
  8. package/build/commonjs/Carousel/components/ItemView.js.map +1 -1
  9. package/build/commonjs/Carousel/components/RootView.js +21 -3
  10. package/build/commonjs/Carousel/components/RootView.js.map +1 -1
  11. package/build/commonjs/Carousel/components/ScrollViewGesture.js +12 -7
  12. package/build/commonjs/Carousel/components/ScrollViewGesture.js.map +1 -1
  13. package/build/commonjs/Carousel/hooks/useAutoplayController.js +4 -1
  14. package/build/commonjs/Carousel/hooks/useAutoplayController.js.map +1 -1
  15. package/build/commonjs/Carousel/hooks/usePagingAnimation.js +57 -19
  16. package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +1 -1
  17. package/build/commonjs/Carousel/types.js.map +1 -1
  18. package/build/commonjs/ViewPager/ViewPagerNative.js +45 -17
  19. package/build/commonjs/ViewPager/ViewPagerNative.js.map +1 -1
  20. package/build/commonjs/ViewPager/ViewPagerProps.js.map +1 -1
  21. package/build/commonjs/ViewPager/ViewPagerWeb.js +7 -7
  22. package/build/commonjs/ViewPager/ViewPagerWeb.js.map +1 -1
  23. package/build/module/Carousel/Carousel.js +12 -7
  24. package/build/module/Carousel/Carousel.js.map +1 -1
  25. package/build/module/Carousel/CarouselProps.js.map +1 -1
  26. package/build/module/Carousel/animation/parallaxItemStyleFactory.js +13 -13
  27. package/build/module/Carousel/animation/parallaxItemStyleFactory.js.map +1 -1
  28. package/build/module/Carousel/components/InternalContext.js.map +1 -1
  29. package/build/module/Carousel/components/ItemView.js +4 -2
  30. package/build/module/Carousel/components/ItemView.js.map +1 -1
  31. package/build/module/Carousel/components/RootView.js +22 -4
  32. package/build/module/Carousel/components/RootView.js.map +1 -1
  33. package/build/module/Carousel/components/ScrollViewGesture.js +12 -7
  34. package/build/module/Carousel/components/ScrollViewGesture.js.map +1 -1
  35. package/build/module/Carousel/hooks/useAutoplayController.js +4 -1
  36. package/build/module/Carousel/hooks/useAutoplayController.js.map +1 -1
  37. package/build/module/Carousel/hooks/usePagingAnimation.js +57 -19
  38. package/build/module/Carousel/hooks/usePagingAnimation.js.map +1 -1
  39. package/build/module/Carousel/types.js.map +1 -1
  40. package/build/module/ViewPager/ViewPagerNative.js +45 -17
  41. package/build/module/ViewPager/ViewPagerNative.js.map +1 -1
  42. package/build/module/ViewPager/ViewPagerProps.js.map +1 -1
  43. package/build/module/ViewPager/ViewPagerWeb.js +8 -8
  44. package/build/module/ViewPager/ViewPagerWeb.js.map +1 -1
  45. package/build/typescript/Carousel/CarouselProps.d.ts +4 -3
  46. package/build/typescript/Carousel/animation/parallaxItemStyleFactory.d.ts +5 -5
  47. package/build/typescript/Carousel/components/InternalContext.d.ts +2 -2
  48. package/build/typescript/Carousel/components/ItemView.d.ts +2 -0
  49. package/build/typescript/Carousel/components/RootView.d.ts +4 -4
  50. package/build/typescript/Carousel/components/ScrollViewGesture.d.ts +1 -1
  51. package/build/typescript/Carousel/hooks/usePagingAnimation.d.ts +3 -3
  52. package/build/typescript/Carousel/types.d.ts +25 -3
  53. package/build/typescript/ViewPager/ViewPagerNative.d.ts +1 -1
  54. package/build/typescript/ViewPager/ViewPagerProps.d.ts +1 -1
  55. package/build/typescript/ViewPager/ViewPagerWeb.d.ts +1 -1
  56. package/package.json +2 -2
  57. package/src/Carousel/Carousel.tsx +7 -6
  58. package/src/Carousel/CarouselProps.ts +4 -3
  59. package/src/Carousel/animation/parallaxItemStyleFactory.ts +23 -23
  60. package/src/Carousel/components/InternalContext.ts +2 -2
  61. package/src/Carousel/components/ItemView.tsx +12 -2
  62. package/src/Carousel/components/RootView.tsx +19 -6
  63. package/src/Carousel/components/ScrollViewGesture.tsx +15 -8
  64. package/src/Carousel/hooks/useAutoplayController.ts +4 -1
  65. package/src/Carousel/hooks/usePagingAnimation.ts +70 -23
  66. package/src/Carousel/types.ts +33 -3
  67. package/src/ViewPager/ViewPagerNative.tsx +49 -16
  68. package/src/ViewPager/ViewPagerProps.ts +1 -1
  69. 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
- (direction: PagingDirection, isGesture?: boolean): void;
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
- * Get current visible item index.
74
+ * Scroll to desired indexed item.
75
+ * Invalid index is ignored.
54
76
  */
55
- getCurrentIndex: GetCurrentIndex;
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" | "UNSTABLE_sharedIndex"> & React.RefAttributes<ViewPagerInstance>>;
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;
@@ -67,6 +67,6 @@ export default interface ViewPagerProps extends ComponentProps<{
67
67
  /**
68
68
  * Unstable API.
69
69
  */
70
- UNSTABLE_sharedIndex?: SharedValue<number>;
70
+ UNSTABLE_sharedPage?: SharedValue<number>;
71
71
  }> {
72
72
  }
@@ -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" | "UNSTABLE_sharedIndex"> & React.RefAttributes<ViewPagerInstance>>;
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.12",
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": "efb9e6cc7cc65e52d1084a0c3664f419dc171240"
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
- startAnimation,
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: startAnimation,
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
- [startAnimation, getCurrentIndex],
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
- startAnimation={startAnimation}
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 width.
50
+ * The item height.
51
+ * For a performance reason, always consider to provide a number value.
51
52
  */
52
- itemHeight: number;
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
- parallaxActiveItemScale?: number;
6
- parallaxActiveItemOpacity?: number;
7
- parallaxAdjacentItemScale?: number;
8
- parallaxAdjacentItemOpacity?: number;
9
- parallaxScrollingOffset?: number;
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
- parallaxActiveItemScale: 0.9,
14
- parallaxActiveItemOpacity: 1,
15
- parallaxAdjacentItemScale: Math.pow(0.9, 2),
16
- parallaxAdjacentItemOpacity: 0.5,
17
- parallaxScrollingOffset: 50,
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
- parallaxActiveItemScale,
23
- parallaxActiveItemOpacity,
24
- parallaxAdjacentItemOpacity,
25
- parallaxAdjacentItemScale,
26
- parallaxScrollingOffset,
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 + parallaxScrollingOffset,
36
+ -itemWidth + scrollingOffset,
37
37
  0,
38
- itemWidth - parallaxScrollingOffset,
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
- parallaxAdjacentItemScale,
52
- parallaxActiveItemScale,
53
- parallaxAdjacentItemScale,
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
- parallaxAdjacentItemOpacity,
62
- parallaxActiveItemOpacity,
63
- parallaxAdjacentItemOpacity,
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: number;
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 { children, index } = props;
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
- { width: itemWidth, height: itemHeight },
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, forwardRef } from 'react';
2
- import type { ViewProps, View } from 'react-native';
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
- overflow: 'hidden',
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: number;
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
- startAnimation: StartPagingAnimation;
14
+ startPagingAnimation: StartPagingAnimation;
15
15
  }
16
16
 
17
- const SCROLL_TO_ADJACENT_TX_THRESHOLD = 60;
18
- const SCROLL_TO_ADJACENT_VX_THRESHOLD = 500;
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
- return Math.abs(translationX) >= SCROLL_TO_ADJACENT_TX_THRESHOLD
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
- startAnimation,
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 userDirection: PagingDirection = shouldScrollToAdjacent(translationX, velocityX)
64
+ const direction: PagingDirection = shouldScrollToAdjacent(translationX, velocityX)
61
65
  ? (translationX < 0 ? 'next' : 'prev')
62
66
  : 'stay';
63
67
 
64
- startAnimation(userDirection, true);
68
+ startPagingAnimation(
69
+ 'directional',
70
+ { direction: direction, isOriginatedFromGesture: true },
71
+ );
65
72
 
66
73
  resumeAutoplay();
67
74
  }
68
- }, [startAnimation, resumeAutoplay]);
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('next', false);
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 { CreateScrollAnimation, GetCurrentIndex, PagingDirection, StartPagingAnimation } from '../types';
4
-
5
- export interface PagingAnimationConfig {
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
- startAnimation: StartPagingAnimation;
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(config: PagingAnimationConfig): 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
- } = config;
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 startAnimation = useCallback((direction: PagingDirection, isGesture: boolean = false) => {
106
- const getToValueByDirection = directionToValue(itemWidth);
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 computeToValueOnNoLoop = (): number => {
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 isGesture
113
- ? getToValueByDirection('stay')
128
+ return isOriginatedFromGesture
129
+ ? getValueByDirectionOnAllAdjacentItemsVisible('stay')
114
130
  : -lastIndex * itemWidth; // last position
115
131
  } else if (currentIndex === lastIndex && direction === 'next') {
116
- return isGesture
117
- ? getToValueByDirection('stay')
132
+ return isOriginatedFromGesture
133
+ ? getValueByDirectionOnAllAdjacentItemsVisible('stay')
118
134
  : lastIndex * itemWidth; // first position
119
135
  }
120
- return getToValueByDirection(direction);
136
+ return getValueByDirectionOnAllAdjacentItemsVisible(direction);
121
137
  };
122
138
 
123
- const toValue = loop ? getToValueByDirection(direction) : computeToValueOnNoLoop();
124
- const animation = createScrollAnimation(offsetTx, toValue);
139
+ const getValueByDirectionalPaging = (_config: DirectionalPagingAnimationConfig): number => {
140
+ const _configWithDefaults: DirectionalPagingAnimationConfig = {
141
+ isOriginatedFromGesture: false,
142
+ ..._config,
143
+ };
125
144
 
126
- animationRef.current = animation;
127
- toValueRef.current = toValue;
145
+ return loop
146
+ ? getValueByDirectionOnAllAdjacentItemsVisible(_configWithDefaults.direction)
147
+ : getValueByDirectionalPagingOnLoopDisabled(_configWithDefaults);
148
+ };
128
149
 
129
- animation.start(({ finished }) => {
130
- if (finished) {
131
- finalizeAnimation();
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
- startAnimation,
193
+ startPagingAnimation,
147
194
  };
148
195
  };
@@ -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
- (direction: PagingDirection, isGesture?: boolean): void;
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
- * Get current visible item index.
93
+ * Scroll to desired indexed item.
94
+ * Invalid index is ignored.
65
95
  */
66
- getCurrentIndex: GetCurrentIndex;
96
+ scrollTo: (option: ScrollToOption) => void;
67
97
  }