diff --git a/README.md b/README.md index a3ad33f..ff151da 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ The context provider that must wrap your carousel components. **All carousel con | `disableInfiniteScroll` | `boolean` | `false` | Disable infinite scrolling (shows first/last slide boundaries) | | `autoScrollAnimation` | `(to: number, duration: number) => number` | `withTiming` | Custom animation function for auto-scroll transitions | | `renderWindow` | `number` | all slides | Slides kept mounted on each side of the current one; for long carousels | +| `centerSlide` | `boolean` | `false` | Centre the current slide when `slideWidth` is narrower, so neighbours peek in | | `children` | `React.ReactNode` | Required | Carousel content (should contain HeroCarousel component) | #### `HeroCarousel` @@ -355,6 +356,18 @@ Different carousel configurations using the compound pattern: > {slides} + +// Centred slides with the neighbours peeking in on both sides. +// Put the gap between slides inside each slide (half on each side), so the content stays centred. + + + {slides.map((slide) => ( + + + + ))} + + ``` ### Using HeroCarousel.AnimatedView diff --git a/src/components/AnimatedPagedView/index.tsx b/src/components/AnimatedPagedView/index.tsx index ebcf824..bcd9a5e 100644 --- a/src/components/AnimatedPagedView/index.tsx +++ b/src/components/AnimatedPagedView/index.tsx @@ -29,7 +29,10 @@ export const AnimatedPagedView = forwardRef { const translateX = useSharedValue(0) const context = useSharedValue({ x: 0 }) - const { slideWidth } = useCarouselContext() + const { slideWidth, centerSlide } = useCarouselContext() + // Starts the row at the carousel's middle, less half a slide. Layout resolves the 50% against the + // carousel's own width, so the current slide is centred from the first frame. + const centerStyle = centerSlide ? { left: '50%' as const, marginLeft: -slideWidth / 2 } : null const childrenArray = React.Children.toArray(props.children) // Worklets must not capture `props`: it holds React elements, which can't be copied to the UI thread. const { onScroll, onScrollBeginDrag } = props @@ -70,7 +73,7 @@ export const AnimatedPagedView = forwardRef translateX.value, @@ -93,8 +96,8 @@ export const AnimatedPagedView = forwardRef - - + + {props.children} diff --git a/src/components/index.ts b/src/components/index.ts index be36b76..59284a0 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1 +1 @@ -export { HeroCarousel, HeroCarouselProps } from './HeroCarousel' +export { HeroCarousel, type HeroCarouselProps } from './HeroCarousel' diff --git a/src/context/CarouselContext/index.tsx b/src/context/CarouselContext/index.tsx index 92e95fa..dfd06ba 100644 --- a/src/context/CarouselContext/index.tsx +++ b/src/context/CarouselContext/index.tsx @@ -41,6 +41,8 @@ type ContextProps = { autoScrollAnimation?: (to: number, duration: number) => number /** Slides kept mounted on each side of the current one. Mounts every slide when omitted. */ renderWindow?: number + /** Centres the current slide when `slideWidth` is narrower than the carousel, so its neighbours peek in on both sides. */ + centerSlide?: boolean } export const CarouselContextProvider = ({ @@ -51,6 +53,7 @@ export const CarouselContextProvider = ({ disableAutoScroll = false, autoScrollAnimation = DEFAULT_ANIMATION, renderWindow, + centerSlide = false, ...rest }: ContextProps) => { const userInteracted = useUserInteracted() @@ -73,6 +76,7 @@ export const CarouselContextProvider = ({ disableAutoScroll, autoScrollAnimation, renderWindow, + centerSlide, }), [ manualScroll, @@ -83,6 +87,7 @@ export const CarouselContextProvider = ({ disableAutoScroll, autoScrollAnimation, renderWindow, + centerSlide, ], )} >