diff --git a/README.md b/README.md index ce89881a..fcc8aa63 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,14 @@ English | [简体中文](./README.zh-CN.md)
## Here comes the official edition! + `v1` has been born, now the carousel will be more natural, and fixed various bugs in the 0.x version, this library will continue to maintain, rest assured to use! [come and experience](https://snack.expo.dev/@zhaodonghao586/simple-carousel) 🎉🎉🎉 Updates: -- Reconstructed some logic, sliding animation more smooth, natural -- timingConfig -> springConfig (The configuration of the 'duration' property is no longer supported by this configuration) -- [...](https://github.com/dohooo/react-native-reanimated-carousel/releases/tag/v1.0.0) +- Reconstructed some logic, sliding animation more smooth, natural +- timingConfig -> springConfig (The configuration of the 'duration' property is no longer supported by this configuration) +- [...](https://github.com/dohooo/react-native-reanimated-carousel/releases/tag/v1.0.0) ## Reason @@ -72,52 +73,50 @@ If use EXPO managed workflow please ensure that the version is greater than 41.B ## Usage ```typescript -import Carousel from "react-native-reanimated-carousel"; +import Carousel from 'react-native-reanimated-carousel'; // ... - width={width} - data={[{ color: "red" }, { color: "purple" }, { color: "yellow" }]} - renderItem={({ color }) => { - return ( - - ); - }} + width={width} + data={[{ color: 'red' }, { color: 'purple' }, { color: 'yellow' }]} + renderItem={({ color }) => { + return ( + + ); + }} />; ``` ## Props -| name | required | default | types | description | -| ----------------------- | -------- | --------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| data | ✅ | | T[] | Carousel items data set | -| width | ✅ | | number | Specified carousel container width | -| renderItem | ✅ | | (data: T, index: number) => React.ReactNode | Render carousel item | -| autoPlay | ❌ | false | boolean | Auto play | -| autoPlayReverse | ❌ | false | boolean | Auto play reverse playback | -| autoPlayInterval | ❌ | 1000 | autoPlayInterval | Auto play playback interval | -| mode | ❌ | defalut | 'default'\|'parallax' | Carousel Animated transitions | -| loop | ❌ | true | boolean | Carousel loop playback | -| parallaxScrollingOffset | ❌ | 100 | number | When use 'parallax' Layout props,this prop can be control prev/next item offset | -| parallaxScrollingScale | ❌ | 0.8 | number | When use 'parallax' Layout props,this prop can be control prev/next item scale | -| style | ❌ | {} | ViewStyle | Carousel container style | -| height | ❌ | '100%' | undefined \| string \| number | Specified carousel container height | -| springConfig | ❌ | {damping: 100} | Animated.WithSpringConfig | Spring config of translation animated | -| onSnapToItem | ❌ | | (index: number) => void | Callback fired when navigating to an item | -| onScrollBegin | ❌ | | () => void | Callback fired when scroll begin | -| onScrollEnd | ❌ | | (previous: number, current: number) => void | Callback fired when scroll end | -| panGestureHandlerProps | ❌ | {} | Omit,'onHandlerStateChange'> | PanGestureHandler props | -| onProgressChange | ❌ | | onProgressChange?: (offsetProgress: number,absoluteProgress: number) => void | On progress change. `offsetProgress`:Total of offset distance (0 390 780 ...); `absoluteProgress`:Convert to index (0 1 2 ...) | - - - +| name | required | default | types | description | +| ----------------------- | -------- | -------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| data | ✅ | | T[] | Carousel items data set | +| width | ✅ | | number | Specified carousel container width | +| renderItem | ✅ | | (data: T, index: number) => React.ReactNode | Render carousel item | +| defaultIndex | ❌ | 0 | number | Default index | +| autoPlay | ❌ | false | boolean | Auto play | +| autoPlayReverse | ❌ | false | boolean | Auto play reverse playback | +| autoPlayInterval | ❌ | 1000 | autoPlayInterval | Auto play playback interval | +| mode | ❌ | defalut | 'default'\|'parallax' | Carousel Animated transitions | +| loop | ❌ | true | boolean | Carousel loop playback | +| parallaxScrollingOffset | ❌ | 100 | number | When use 'parallax' Layout props,this prop can be control prev/next item offset | +| parallaxScrollingScale | ❌ | 0.8 | number | When use 'parallax' Layout props,this prop can be control prev/next item scale | +| style | ❌ | {} | ViewStyle | Carousel container style | +| height | ❌ | '100%' | undefined \| string \| number | Specified carousel container height | +| springConfig | ❌ | {damping: 100} | Animated.WithSpringConfig | Spring config of translation animated | +| onSnapToItem | ❌ | | (index: number) => void | Callback fired when navigating to an item | +| onScrollBegin | ❌ | | () => void | Callback fired when scroll begin | +| onScrollEnd | ❌ | | (previous: number, current: number) => void | Callback fired when scroll end | +| panGestureHandlerProps | ❌ | {} | Omit,'onHandlerStateChange'> | PanGestureHandler props | +| onProgressChange | ❌ | | onProgressChange?: (offsetProgress: number,absoluteProgress: number) => void | On progress change. `offsetProgress`:Total of offset distance (0 390 780 ...); `absoluteProgress`:Convert to index (0 1 2 ...) | ## Ref diff --git a/README.zh-CN.md b/README.zh-CN.md index 4386c66d..ade6df8b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -17,12 +17,14 @@
## 正式版来了! -`v1`已经诞生,现在轮播图的滚动将会更加自然,并且修复了0.x版本中出现的各种bug,此库将会持续维护,放心使用! [快来体验](https://snack.expo.dev/@zhaodonghao586/simple-carousel) 🎉🎉🎉 + +`v1`已经诞生,现在轮播图的滚动将会更加自然,并且修复了 0.x 版本中出现的各种 bug,此库将会持续维护,放心使用! [快来体验](https://snack.expo.dev/@zhaodonghao586/simple-carousel) 🎉🎉🎉 更新: -- 重构了部分逻辑,滑动动画更加流畅、自然 -- timingConfig -> springConfig (此配置不再支持对`duration`属性的配置) -- [...](https://github.com/dohooo/react-native-reanimated-carousel/releases/tag/v1.0.0) + +- 重构了部分逻辑,滑动动画更加流畅、自然 +- timingConfig -> springConfig (此配置不再支持对`duration`属性的配置) +- [...](https://github.com/dohooo/react-native-reanimated-carousel/releases/tag/v1.0.0) ## 原因 @@ -71,49 +73,50 @@ npm install react-native-reanimated-carousel ## 使用 ```typescript -import Carousel from "react-native-reanimated-carousel"; +import Carousel from 'react-native-reanimated-carousel'; // ... - width={width} - data={[{ color: "red" }, { color: "purple" }, { color: "yellow" }]} - renderItem={({ color }) => { - return ( - - ); - }} + width={width} + data={[{ color: 'red' }, { color: 'purple' }, { color: 'yellow' }]} + renderItem={({ color }) => { + return ( + + ); + }} />; ``` ## Props -| name | required | default | types | description | -| ----------------------- | -------- | --------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- | -| data | ✅ | | T[] | 即将渲染的数据集合 | -| width | ✅ | | number | 轮播图容器的宽度 | -| renderItem | ✅ | | (data: T, index: number) => React.ReactNode | 渲染元素的方法 | -| autoPlay | ❌ | false | boolean | 是否自动播放 | -| autoPlayReverse | ❌ | false | boolean | 是否倒序自动播放 | -| autoPlayInterval | ❌ | 1000 | autoPlayInterval | 自动播放的间隔 | -| mode | ❌ | defalut | 'default'\|'parallax' | 轮播图播放模式,`default`为默认无任何 UI 效果,演示图片使用的`parallax` | -| loop | ❌ | true | boolean | 是否循环播放 | -| parallaxScrollingOffset | ❌ | 100 | number | 当使用 mode=`parallax`,这个属性可以控制两侧图片离中间元素的距离 | -| parallaxScrollingScale | ❌ | 0.8 | number | 当使用 mode=`parallax`,这个属性可以控制两侧图片的缩放比例 | -| style | ❌ | {} | ViewStyle | 轮播图容器样式 | -| height | ❌ | '100%' | undefined \| string \| number | 指定轮播图容器高度 | -| springConfig | ❌ | {damping: 100} | Animated.WithSpringConfig | 配置动画效果 | -| onSnapToItem | ❌ | | (index: number) => void | 切换至另一张轮播图时触发 | -| onScrollBegin | ❌ | | () => void | 切换动画开始时触发 | -| onScrollEnd | ❌ | | (previous: number, current: number) => void | 切换动画结束时触发 | -| panGestureHandlerProps | ❌ | {} | Omit,'onHandlerStateChange'> | PanGestureHandler props | -| onProgressChange | ❌ | | onProgressChange?: (offsetProgress: number,absoluteProgress: number) => void | 当滚动进度发生变化时触发 `offsetProgress`:总的偏移值 (0 390 780 ...); `absoluteProgress`:转化为index的进度变化 (0 1 2 ...) | +| name | required | default | types | description | +| ----------------------- | -------- | -------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| data | ✅ | | T[] | 即将渲染的数据集合 | +| width | ✅ | | number | 轮播图容器的宽度 | +| renderItem | ✅ | | (data: T, index: number) => React.ReactNode | 渲染元素的方法 | +| defaultIndex | ❌ | 0 | number | 默认 index | +| autoPlay | ❌ | false | boolean | 是否自动播放 | +| autoPlayReverse | ❌ | false | boolean | 是否倒序自动播放 | +| autoPlayInterval | ❌ | 1000 | autoPlayInterval | 自动播放的间隔 | +| mode | ❌ | defalut | 'default'\|'parallax' | 轮播图播放模式,`default`为默认无任何 UI 效果,演示图片使用的`parallax` | +| loop | ❌ | true | boolean | 是否循环播放 | +| parallaxScrollingOffset | ❌ | 100 | number | 当使用 mode=`parallax`,这个属性可以控制两侧图片离中间元素的距离 | +| parallaxScrollingScale | ❌ | 0.8 | number | 当使用 mode=`parallax`,这个属性可以控制两侧图片的缩放比例 | +| style | ❌ | {} | ViewStyle | 轮播图容器样式 | +| height | ❌ | '100%' | undefined \| string \| number | 指定轮播图容器高度 | +| springConfig | ❌ | {damping: 100} | Animated.WithSpringConfig | 配置动画效果 | +| onSnapToItem | ❌ | | (index: number) => void | 切换至另一张轮播图时触发 | +| onScrollBegin | ❌ | | () => void | 切换动画开始时触发 | +| onScrollEnd | ❌ | | (previous: number, current: number) => void | 切换动画结束时触发 | +| panGestureHandlerProps | ❌ | {} | Omit,'onHandlerStateChange'> | PanGestureHandler props | +| onProgressChange | ❌ | | onProgressChange?: (offsetProgress: number,absoluteProgress: number) => void | 当滚动进度发生变化时触发 `offsetProgress`:总的偏移值 (0 390 780 ...); `absoluteProgress`:转化为 index 的进度变化 (0 1 2 ...) | ## Ref diff --git a/example/src/App.tsx b/example/src/App.tsx index 19319f1e..b287adf8 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -32,6 +32,7 @@ export default function App() { > + defaultIndex={1} ref={r} width={width} data={data} diff --git a/src/Carousel.tsx b/src/Carousel.tsx index 4c797c43..3467592e 100644 --- a/src/Carousel.tsx +++ b/src/Carousel.tsx @@ -17,10 +17,11 @@ import Animated, { import { CarouselItem } from './CarouselItem'; import type { TMode } from './layouts'; import { ParallaxLayout } from './layouts/index'; -import { useCarouselController } from './useCarouselController'; -import { useComputedAnim } from './useComputedAnim'; -import { useAutoPlay } from './useAutoPlay'; -import { useIndexController } from './useIndexController'; +import { useCarouselController } from './hooks/useCarouselController'; +import { useComputedAnim } from './hooks/useComputedAnim'; +import { useAutoPlay } from './hooks/useAutoPlay'; +import { useIndexController } from './hooks/useIndexController'; +import { usePropsErrorBoundary } from './hooks/usePropsErrorBoundary'; const defaultSpringConfig: Animated.WithSpringConfig = { damping: 100, @@ -37,10 +38,6 @@ export interface ICarouselProps { * @default 'default' */ mode?: TMode; - /** - * Render carousel item. - */ - renderItem: (data: T, index: number) => React.ReactNode; /** * Specified carousel container width. */ @@ -54,6 +51,11 @@ export interface ICarouselProps { * Carousel items data set. */ data: T[]; + /** + * Default index + * @default 0 + */ + defaultIndex?: number; /** * Auto play */ @@ -93,6 +95,10 @@ export interface ICarouselProps { Partial, 'onHandlerStateChange' >; + /** + * Render carousel item. + */ + renderItem: (data: T, index: number) => React.ReactNode; /** * Callback fired when navigating to an item */ @@ -140,6 +146,7 @@ function Carousel( ref: React.Ref ) { const { + defaultIndex = 0, height = '100%', data: _data = [], loop = true, @@ -156,12 +163,32 @@ function Carousel( onProgressChange, } = props; + usePropsErrorBoundary({ + ...props, + defaultIndex, + height, + data: _data, + loop, + mode, + autoPlay, + autoPlayReverse, + autoPlayInterval, + parallaxScrollingOffset, + parallaxScrollingScale, + style, + panGestureHandlerProps, + // @ts-ignore + renderItem, + onSnapToItem, + onProgressChange, + }); + const timingConfig = { ...defaultSpringConfig, ...props.springConfig, }; const width = Math.round(props.width); - const handlerOffsetX = useSharedValue(0); + const handlerOffsetX = useSharedValue(defaultIndex * width); const data = React.useMemo(() => { if (!loop) return _data; diff --git a/src/CarouselItem.tsx b/src/CarouselItem.tsx index 2dcf6a49..d12e82da 100644 --- a/src/CarouselItem.tsx +++ b/src/CarouselItem.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { FlexStyle, View } from 'react-native'; import Animated, { useAnimatedStyle } from 'react-native-reanimated'; -import type { IComputedAnimResult } from './useComputedAnim'; -import { useOffsetX } from './useOffsetX'; +import type { IComputedAnimResult } from './hooks/useComputedAnim'; +import { useOffsetX } from './hooks/useOffsetX'; export const CarouselItem: React.FC<{ loop?: boolean; diff --git a/src/useAutoPlay.ts b/src/hooks/useAutoPlay.ts similarity index 100% rename from src/useAutoPlay.ts rename to src/hooks/useAutoPlay.ts diff --git a/src/useCarouselController.tsx b/src/hooks/useCarouselController.tsx similarity index 100% rename from src/useCarouselController.tsx rename to src/hooks/useCarouselController.tsx diff --git a/src/useComputedAnim.ts b/src/hooks/useComputedAnim.ts similarity index 100% rename from src/useComputedAnim.ts rename to src/hooks/useComputedAnim.ts diff --git a/src/useIndexController.ts b/src/hooks/useIndexController.ts similarity index 100% rename from src/useIndexController.ts rename to src/hooks/useIndexController.ts diff --git a/src/useOffsetX.ts b/src/hooks/useOffsetX.ts similarity index 100% rename from src/useOffsetX.ts rename to src/hooks/useOffsetX.ts diff --git a/src/hooks/usePropsErrorBoundary.ts b/src/hooks/usePropsErrorBoundary.ts new file mode 100644 index 00000000..562a84f2 --- /dev/null +++ b/src/hooks/usePropsErrorBoundary.ts @@ -0,0 +1,15 @@ +import React from 'react'; +import type { ICarouselProps } from 'src/Carousel'; + +export function usePropsErrorBoundary(props: ICarouselProps) { + React.useEffect(() => { + const { defaultIndex, data } = props; + if (typeof defaultIndex === 'number') { + if (defaultIndex < 0 || defaultIndex >= data.length) { + throw Error( + 'DefaultIndex must be in the range of data length.' + ); + } + } + }, [props]); +} diff --git a/src/layouts/ParallaxLayout.tsx b/src/layouts/ParallaxLayout.tsx index 8cdf134d..e21f078c 100644 --- a/src/layouts/ParallaxLayout.tsx +++ b/src/layouts/ParallaxLayout.tsx @@ -6,8 +6,8 @@ import Animated, { interpolate, useAnimatedStyle, } from 'react-native-reanimated'; -import type { IComputedAnimResult } from 'src/useComputedAnim'; -import { useOffsetX } from '../useOffsetX'; +import type { IComputedAnimResult } from 'src/hooks/useComputedAnim'; +import { useOffsetX } from '../hooks/useOffsetX'; export const ParallaxLayout: React.FC<{ loop?: boolean;