Skip to content

Commit

Permalink
feat: support to custom carousel animations by customAnimation `cus…
Browse files Browse the repository at this point in the history
…tomConfig` props
  • Loading branch information
dohooo committed Jan 8, 2022
1 parent b99deca commit eb3082b
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 44 deletions.
4 changes: 3 additions & 1 deletion docs/props.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
| width | vertical ❌ horizontal ✅ | '100%' | number \| undefined | Specified carousel item width |
| height | vertical ✅ horizontal ❌ | '100%' | number \| undefined | Specified carousel item height |
| mode || defalut | 'horizontal-stack'\|'vertical-stack'\|'parallax' | Carousel Animated transitions |
| modeConfig || | | Different modes correspond to different configurations. For details, see below[modeConfig](#`modeConfig` Props) |
| modeConfig || | | Different modes correspond to different configurations. For details, see below[modeConfig](#`modeConfig` Props) |
| style || {} | ViewStyle | Carousel container style |
| defaultIndex || 0 | number | Default index |
| autoPlay || false | boolean | Auto play |
Expand All @@ -25,6 +25,8 @@
| showLength || data.length - 1 | number | The maximum number of items will show in stack |
| pagingEnabled || true | boolean | When true, the scroll view stops on multiples of the scroll view's size when scrolling |
| enableSnap || true | boolean | If enabled, releasing the touch will scroll to the nearest item, valid when pagingEnabled=false |
| customConfig || | () => {type?: 'negative' \| 'positive';viewCount?: number;} | Custom carousel config |
| customAnimation || | (value: number) => Animated.AnimatedStyleProp<ViewStyle> | Custom animations. For details, see below[modeConfig](todo) |

## `modeConfig` Props

Expand Down
2 changes: 2 additions & 0 deletions docs/props.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
| showLength || data.length - 1 | number | 堆栈视图中展示元素的最大数量 |
| pagingEnabled || true | boolean | 当值为 true 时,滚动条会停在滚动视图的尺寸的整数倍位置。 |
| enableSnap || true | boolean | 如果启用,松开触摸会滚动到最近的元素,当 pagingEnabled=false 时有效 |
| customConfig || | () => {type?: 'negative' \| 'positive';viewCount?: number;} | 自定义轮播图内部配置 |
| customAnimation || | (value: number) => Animated.AnimatedStyleProp<ViewStyle> | 自定动画,详情见[customAnimation demo](todo) |

## `modeConfig` Props

Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": true,
"main": "index",
"scripts": {
"android": "expo start --android",
"android:pretty": "expo start --android",
"ios": "expo start -c --ios",
"ios:pretty": "PRETTY=true expo start -c --ios",
"web": "expo start --web",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"prepare": "bob build",
"release": "release-it",
"ios": "yarn --cwd example ios",
"ios:pretty": "yarn --cwd example ios:pretty",
"android": "yarn --cwd example android",
"android:pretty": "yarn --cwd example android:pretty",
"pods": "cd example && pod-install --quiet",
"bootstrap": "yarn example && yarn && yarn pods"
},
Expand Down
12 changes: 10 additions & 2 deletions src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function Carousel<T>(
onSnapToItem,
onScrollBegin,
onProgressChange,
customAnimation,
} = props;

const commonVariables = useCommonVariables(props);
Expand Down Expand Up @@ -143,13 +144,20 @@ function Carousel<T>(
index={i}
handlerOffsetX={offsetX}
visibleRanges={visibleRanges}
animationStyle={layoutConfig}
animationStyle={customAnimation || layoutConfig}
>
{renderItem(item, realIndex)}
</BaseLayout>
);
},
[data, offsetX, renderItem, layoutConfig, visibleRanges]
[
data,
offsetX,
visibleRanges,
renderItem,
layoutConfig,
customAnimation,
]
);

return (
Expand Down
1 change: 0 additions & 1 deletion src/hooks/useInitProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export function useInitProps<T>(
props.modeConfig.showLength =
props.modeConfig?.showLength ?? data.length - 1;
}

return {
...props,
defaultIndex,
Expand Down
29 changes: 18 additions & 11 deletions src/layouts/BaseLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import Animated, {
import { IOpts, useOffsetX } from '../hooks/useOffsetX';
import type { IVisibleRanges } from '../hooks/useVisibleRanges';
import { LazyView } from '../LazyView';
import type { ILayoutConfig as IStackLayoutConfig } from './stack';
import { CTX } from '../store';
import type { ILayoutConfig } from './stack';

export type TAnimationStyle = (
value: number
Expand All @@ -24,24 +24,32 @@ export const BaseLayout: React.FC<{
const { handlerOffsetX, index, children, visibleRanges, animationStyle } =
props;

const context = React.useContext(CTX);
const {
props: { mode, loop, data, width, height, vertical, modeConfig },
} = React.useContext(CTX);

const [shouldUpdate, setShouldUpdate] = React.useState(false);

props: {
loop,
data,
width,
height,
vertical,
customConfig,
mode,
modeConfig,
},
} = context;
const size = vertical ? height : width;

const [shouldUpdate, setShouldUpdate] = React.useState(false);
let offsetXConfig: IOpts = {
handlerOffsetX,
index,
size,
data,
loop,
...(typeof customConfig === 'function' ? customConfig() : {}),
};

if (mode === 'horizontal-stack') {
const { snapDirection, showLength } = modeConfig as IStackLayoutConfig;
const { snapDirection, showLength } = modeConfig as ILayoutConfig;

offsetXConfig = {
handlerOffsetX,
Expand All @@ -55,8 +63,7 @@ export const BaseLayout: React.FC<{
}

const x = useOffsetX(offsetXConfig, visibleRanges);

const _animatedStyle = useAnimatedStyle(
const animatedStyle = useAnimatedStyle(
() => animationStyle(x.value / size),
[animationStyle]
);
Expand Down Expand Up @@ -90,7 +97,7 @@ export const BaseLayout: React.FC<{
height: height || '100%',
position: 'absolute',
},
_animatedStyle,
animatedStyle,
]}
>
<LazyView shouldUpdate={shouldUpdate}>{children}</LazyView>
Expand Down
11 changes: 10 additions & 1 deletion src/layouts/parallax.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Extrapolate, interpolate } from 'react-native-reanimated';
import type { ComputedDirectionTypes } from '../types';

type TBaseConfig = {
size: number;
vertical: boolean;
};

export interface ILayoutConfig {
interface ILayoutConfig {
/**
* When use default Layout props,this prop can be control prev/next item offset.
* @default 100
Expand All @@ -18,6 +19,14 @@ export interface ILayoutConfig {
parallaxScrollingScale?: number;
}

export type TParallaxModeProps = ComputedDirectionTypes<{
/**
* Carousel Animated transitions.
*/
mode?: 'parallax';
modeConfig?: ILayoutConfig;
}>;

export function parallaxLayout(
baseConfig: TBaseConfig,
modeConfig: ILayoutConfig = {}
Expand Down
42 changes: 42 additions & 0 deletions src/layouts/stack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useMemo } from 'react';
import { Dimensions, TransformsStyle, ViewStyle } from 'react-native';
import { Extrapolate, interpolate } from 'react-native-reanimated';
import type { ComputedDirectionTypes, CustomConfig } from 'src/types';

const screen = Dimensions.get('window');

Expand All @@ -13,6 +15,24 @@ export interface ILayoutConfig {
snapDirection?: 'left' | 'right';
}

export type TStackModeProps = ComputedDirectionTypes<{
/**
* Carousel Animated transitions.
*/
mode?: 'horizontal-stack' | 'vertical-stack';
/**
* Stack animation style.
* @default
* mode: 'vertical',
* snapDirection: 'right',
* moveSize: window.width,
* stackInterval: 30,
* scaleInterval: 0.08,
* rotateZDeg: 135,
*/
modeConfig?: ILayoutConfig;
}>;

export function horizontalStackLayout(modeConfig: ILayoutConfig = {}) {
return (_value: number) => {
'worklet';
Expand Down Expand Up @@ -106,6 +126,28 @@ export function horizontalStackLayout(modeConfig: ILayoutConfig = {}) {
};
}

export function useHorizontalStackLayout(
customAnimationConfig: ILayoutConfig = {},
customConfig: CustomConfig = {}
) {
const config = useMemo(
() => ({
type:
customAnimationConfig.snapDirection === 'right'
? 'negative'
: 'positive',
viewCount: customAnimationConfig.showLength,
...customConfig,
}),
[customAnimationConfig, customConfig]
);

return {
layout: horizontalStackLayout(customAnimationConfig),
config,
};
}

export function verticalStackLayout(modeConfig: ILayoutConfig = {}) {
return (_value: number) => {
'worklet';
Expand Down
43 changes: 16 additions & 27 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { ViewStyle } from 'react-native';
import type { PanGestureHandlerProps } from 'react-native-gesture-handler';
import type { ILayoutConfig as IStackLayoutConfig } from './layouts/stack';
import type { ILayoutConfig as IParallaxLayoutConfig } from './layouts/parallax';
import type Animated from 'react-native-reanimated';
import type { TParallaxModeProps } from './layouts/parallax';
import type { TStackModeProps } from './layouts/stack';

export type ComputedDirectionTypes<T, VP = {}, HP = {}> =
| (T &
Expand Down Expand Up @@ -35,31 +36,10 @@ export type ComputedDirectionTypes<T, VP = {}, HP = {}> =
height?: number;
});

type TParallaxModeProps = ComputedDirectionTypes<{
/**
* Carousel Animated transitions.
*/
mode?: 'parallax';
modeConfig?: IParallaxLayoutConfig;
}>;

type TStackModeProps = ComputedDirectionTypes<{
/**
* Carousel Animated transitions.
*/
mode?: 'horizontal-stack' | 'vertical-stack';
/**
* Stack animation style.
* @default
* mode: 'vertical',
* snapDirection: 'right',
* moveSize: window.width,
* stackInterval: 30,
* scaleInterval: 0.08,
* rotateZDeg: 135,
*/
modeConfig?: IStackLayoutConfig;
}>;
export type CustomConfig = {
type?: 'negative' | 'positive';
viewCount?: number;
};

export type TCarouselProps<T = any> = {
ref?: React.Ref<ICarouselInstance>;
Expand Down Expand Up @@ -120,6 +100,15 @@ export type TCarouselProps<T = any> = {
* @default true
*/
enableSnap?: boolean;
/**
* Custom carousel config.
*/
customConfig?: () => CustomConfig;
/**
* Custom animations.
* Must use `worklet`, Details: https://docs.swmansion.com/react-native-reanimated/docs/2.2.0/worklets/
*/
customAnimation?: (value: number) => Animated.AnimatedStyleProp<ViewStyle>;
/**
* Render carousel item.
*/
Expand Down

0 comments on commit eb3082b

Please sign in to comment.