diff --git a/README.md b/README.md index 410d105..5c027ac 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ A highly customizable, performant carousel component for React Native with advanced animations, auto-scrolling capabilities, and infinite scrolling support. Built with React Native Reanimated for smooth, native-level performance. -**✨ Context-Based Configuration** - All carousel settings are configured through the context provider for a clean, centralized API. +**✨ Compound Pattern** - Clean, intuitive API with `HeroCarousel.Provider`, `HeroCarousel.Item`, and `HeroCarousel.AnimatedView` +**✨ Context-Based Configuration** - All carousel settings are configured through the provider for a clean, centralized API. ## Features @@ -27,7 +28,7 @@ npm install @strv/react-native-hero-carousel # or yarn add @strv/react-native-hero-carousel # or -yarn add @strv/react-native-hero-carousel +pnpm add @strv/react-native-hero-carousel ``` ### Peer Dependencies @@ -45,7 +46,7 @@ Make sure to follow the [React Native Reanimated installation guide](https://doc ```tsx import React from 'react' import { View, Text, StyleSheet } from 'react-native' -import { HeroCarousel, CarouselContextProvider } from '@strv/react-native-hero-carousel' +import { HeroCarousel } from '@strv/react-native-hero-carousel' const slides = [ { id: 1, title: 'Slide 1', color: '#FF6B6B' }, @@ -62,7 +63,7 @@ const Slide = ({ title, color }: { title: string; color: string }) => ( export default function BasicCarousel() { return ( - + {slides.map((slide) => ( @@ -70,7 +71,7 @@ export default function BasicCarousel() { ))} - + ) } @@ -95,12 +96,22 @@ const styles = StyleSheet.create({ ### Components -#### `CarouselContextProvider` +The `HeroCarousel` component uses a **compound pattern** that provides a clean, intuitive API: + +```tsx + + + {/* Your slide content */} + + +``` + +#### `HeroCarousel.Provider` The context provider that must wrap your carousel components. **All carousel configuration is passed here.** ```tsx - withTiming(to, { duration })} // Custom animation > {children} - + ``` **Props:** @@ -126,7 +137,7 @@ The context provider that must wrap your carousel components. **All carousel con #### `HeroCarousel` -The main carousel component that renders slides. **Takes no configuration props** - all configuration is handled by the context. +The main carousel component that renders slides. **Takes no configuration props** - all configuration is handled by the context provider. ```tsx @@ -142,6 +153,42 @@ The main carousel component that renders slides. **Takes no configuration props* | ---------- | ------------------- | ------------------------- | | `children` | `React.ReactNode[]` | Array of slide components | +#### `HeroCarousel.Item` + +A wrapper component for individual slides. Provides slide context to child components. **Note:** This is automatically used internally when you pass children to `HeroCarousel`, but you can use it directly for more control. + +```tsx +{/* Your slide content */} +``` + +#### `HeroCarousel.AnimatedView` + +A specialized animated view component that automatically handles entering/exiting animations based on carousel scroll position. Perfect for creating slide-specific animations. + +```tsx +import { FadeIn, FadeOut } from 'react-native-reanimated' +; + This animates when the slide becomes active + +``` + +**Props:** + +| Prop | Type | Default | Description | +| ------------------------- | -------------------------------------- | -------- | -------------------------------------------------------- | +| `children` | `React.ReactNode` | Required | Content to animate | +| `entering` | `AnimatedProps['entering']` | - | Entering animation (from react-native-reanimated) | +| `exiting` | `AnimatedProps['exiting']` | - | Exiting animation (from react-native-reanimated) | +| `layout` | `AnimatedProps['layout']` | - | Layout animation (from react-native-reanimated) | +| `enteringThreshold` | `number` | `0.99` | Threshold (0-1) when entering animation should trigger | +| `exitingThreshold` | `number` | `0.01` | Threshold (0-1) when exiting animation should trigger | +| `keepVisibleAfterExiting` | `boolean` | `false` | Keep component visible after exiting animation completes | +| `style` | `AnimatedProps['style']` | - | Additional styles | + ### Hooks #### `useCarouselContext()` @@ -161,12 +208,12 @@ const { scrollValue, timeoutValue, slideWidth, userInteracted, setUserInteracted - `userInteracted`: Boolean indicating if user has interacted with carousel - `setUserInteracted`: Function to update interaction state -#### `useHeroCarouselSlideIndex()` +#### `useAutoCarouselSlideIndex()` -Get the current slide information and auto-scroll controls. +Get the current slide information and auto-scroll controls. Must be used within a slide component (inside `HeroCarousel`). ```tsx -const { index, total, runAutoScroll, goToPage } = useHeroCarouselSlideIndex() +const { index, total, runAutoScroll, goToPage } = useAutoCarouselSlideIndex() ``` **Returns:** @@ -176,30 +223,52 @@ const { index, total, runAutoScroll, goToPage } = useHeroCarouselSlideIndex() - `runAutoScroll`: Function to manually trigger auto-scroll with custom interval - `goToPage`: Function to programmatically navigate to a specific slide from another slide -### Utilities - -#### `interpolateInsideCarousel()` +#### `useInterpolateInsideCarousel()` -Advanced interpolation utility for creating custom slide animations. +Hook for creating custom slide animations with automatic interpolation based on carousel scroll position. Must be used within a slide component (inside `HeroCarousel`). Returns a `SharedValue` that you can use in animated styles. ```tsx -import { interpolateInsideCarousel } from '@strv/react-native-hero-carousel' +import { useInterpolateInsideCarousel } from '@strv/react-native-hero-carousel' +import Animated, { useAnimatedStyle, interpolate, Extrapolation } from 'react-native-reanimated' -const animatedStyle = useAnimatedStyle(() => { - const progress = interpolateInsideCarousel(scrollValue.value, slideIndex, total, { +const Slide = () => { + const progress = useInterpolateInsideCarousel({ valueBefore: 0, // Value for slides before current thisValue: 1, // Value for current slide valueAfter: 0, // Value for slides after current offset: 0.2, // Animation offset (optional) }) - return { - opacity: progress, - transform: [{ scale: progress }], - } -}) + const animatedStyle = useAnimatedStyle(() => { + return { + opacity: progress.value, + transform: [ + { + scale: interpolate(progress.value, [0, 1], [0.8, 1], Extrapolation.CLAMP), + }, + ], + } + }) + + return {/* Your content */} +} ``` +**Returns:** + +A `SharedValue` (from `useDerivedValue`) representing the interpolated progress value (0-1) based on the current slide's position in the carousel. Access the value using `.value` in animated styles or worklet functions. + +**Parameters:** + +| Parameter | Type | Default | Description | +| ------------- | -------- | -------- | ---------------------------------------------------------------- | +| `valueBefore` | `number` | Required | Value to use for slides before the current slide | +| `thisValue` | `number` | Required | Value to use for the current slide | +| `valueAfter` | `number` | Required | Value to use for slides after the current slide | +| `offset` | `number` | `0` | Animation offset (0-1) to control when the animation starts/ends | + +**Note:** The `interpolateInsideCarousel` utility function is now internal-only. Use this hook instead for all custom animations. The hook automatically handles the slide context (index, total, scrollValue) internally. + ## Examples We provide a comprehensive example app showcasing all the carousel features. You can run the examples locally or view the source code: @@ -216,23 +285,25 @@ Then scan the QR code with Expo Go or run on simulator. See the [example app REA ### 📱 Available Examples -| Example | Description | Source Code | -| ---------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------- | -| **Basic Carousel** | Simple auto-scrolling image carousel | [`BasicExample.tsx`](./example/examples/BasicExample.tsx) | -| **Animated Carousel** | Custom animations with scale, rotation, and opacity | [`AnimatedExample.tsx`](./example/examples/AnimatedExample.tsx) | -| **Video Carousel** | Video playback with play/pause controls | [`VideoCarouselExample.tsx`](./example/examples/VideoCarouselExample.tsx) | -| **Timer Pagination** | Visual progress indicators with custom intervals | [`TimerPaginationExample.tsx`](./example/examples/TimerPaginationExample.tsx) | -| **Entering Animation** | Advanced slide entrance animations | [`EnteringAnimationExample.tsx`](./example/examples/EnteringAnimationExample.tsx) | -| **Offset Example** | Custom slide positioning and spacing | [`OffsetExample.tsx`](./example/examples/OffsetExample.tsx) | +| Example | Description | Source Code | +| ---------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| **Basic Carousel** | Simple auto-scrolling image carousel | [`BasicExample.tsx`](./example/examples/BasicExample.tsx) | +| **Animated Carousel** | Custom animations with scale, rotation, and opacity | [`AnimatedExample.tsx`](./example/examples/AnimatedExample.tsx) | +| **Video Carousel** | Video playback with play/pause controls | [`VideoCarouselExample.tsx`](./example/examples/VideoCarouselExample.tsx) | +| **Timer Pagination** | Visual progress indicators with custom intervals | [`TimerPaginationExample.tsx`](./example/examples/TimerPaginationExample.tsx) | +| **Entering Animation** | Advanced slide entrance animations using `HeroCarousel.AnimatedView` | [`EnteringAnimationExample.tsx`](./example/examples/EnteringAnimationExample.tsx) | +| **Offset Example** | Custom slide positioning and spacing | [`OffsetExample.tsx`](./example/examples/OffsetExample.tsx) | ### 🎯 Key Example Features - **Image Carousels** with smooth transitions and auto-scrolling - **Video Integration** with `expo-video` and playback controls -- **Custom Animations** using `interpolateInsideCarousel` utility +- **Custom Animations** using `useInterpolateInsideCarousel` hook +- **Entering/Exiting Animations** using `HeroCarousel.AnimatedView` component - **Timer-based Pagination** with visual progress bars - **Gesture Handling** with swipe navigation and user interaction detection - **Performance Optimization** with image preloading and memoization +- **Compound Pattern** - All examples use `HeroCarousel.Provider` for configuration ### 📍 Pagination Examples @@ -253,38 +324,87 @@ All pagination components automatically sync with the carousel state and support ### Configuration Examples -Different carousel configurations using the context provider: +Different carousel configurations using the compound pattern: ```tsx // Basic auto-scrolling carousel - + {slides} - + // Video carousel without auto-scroll - + {videoSlides} - + // Carousel with custom intervals per slide - (index + 1) * 2000}> + (index + 1) * 2000}> {slides} - + // Carousel starting from specific slide - + {slides} - + // Custom slide width and animation - withSpring(to, { damping: 15 })} > {slides} - + ``` +### Using HeroCarousel.AnimatedView + +The `HeroCarousel.AnimatedView` component automatically handles entering/exiting animations based on carousel scroll position. Perfect for creating slide-specific animations: + +```tsx +import { HeroCarousel } from '@strv/react-native-hero-carousel' +import { FadeIn, FadeOut, SlideInDown } from 'react-native-reanimated' + +const Slide = ({ title, image }: { title: string; image: string }) => ( + + + + {/* Content that animates when slide becomes active */} + + {title} + + + {/* Multiple animated views with different timings */} + + Subtitle with delay + + +) + +// Usage + + + {slides.map((slide) => ( + + ))} + + +``` + +**Key Features:** + +- Automatically triggers entering animation when slide becomes active +- Triggers exiting animation when slide leaves view +- Supports all Reanimated entering/exiting animations +- Configurable thresholds for animation timing +- Can keep content visible after exiting animation + ### Programmatic Navigation Control the carousel programmatically using the context: @@ -292,7 +412,7 @@ Control the carousel programmatically using the context: ```tsx const CarouselWithControls = () => { const { scrollValue, goToPage } = useCarouselContext() - const { runAutoScroll } = useHeroCarouselSlideIndex() + const { runAutoScroll } = useAutoCarouselSlideIndex() const goToNext = () => { runAutoScroll(0) // Immediate transition @@ -303,7 +423,7 @@ const CarouselWithControls = () => { } return ( - + {/* Your slides */} @@ -312,7 +432,7 @@ const CarouselWithControls = () => {