react-native-momentum-carousel
Version:
A React Native carousel component enables smooth and interactive image or content sliders with swiping capabilities. Ideal for showcasing multiple items or images in a compact space, this carousel can be customized with features like infinite scrolling, p
192 lines (182 loc) • 7.65 kB
text/typescript
import { useMemo } from 'react';
import { ItemCarouselProps } from './ItemCarousel';
import {
useAnimatedStyle,
interpolate,
Extrapolation,
} from 'react-native-reanimated';
/**
* useLayoutStackAnimation calculates the animated style for a stack-style carousel animation.
* It creates an opacity and scaling effect for each carousel item based on the scroll position (scrollX).
* The items will scale down when not in the center and will move slightly to the left or right as they approach the center.
*/
export function useLayoutStackAnimation(data: ItemCarouselProps) {
// Calculate the input range based on the current index and direction (vertical/horizontal)
const inputRange = useMemo(() => {
const currentIndex = data.info.index;
return [
(currentIndex - 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // previous item position
currentIndex * (!data.vertical ? data.itemWidth : data.itemHeight), // current item position
(currentIndex + 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // next item position
];
}, [data.info.index, data.itemHeight, data.itemWidth, data.vertical]);
// Animated style that adjusts opacity, scale, and translation based on scroll position
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(
data.scrollX.value, // The scroll position value used to interpolate
inputRange,
[0.8, 1, 0.8], // Decrease opacity when items are not in the center
Extrapolation.CLAMP // Prevent extrapolation beyond the defined range
),
transform: [
{
scale: interpolate(
data.scrollX.value,
inputRange,
[
data.inactiveScale ? data.inactiveScale : 0.8, // Scale down inactive items
1, // Scale to 1 when the item is centered
data.inactiveScale ? data.inactiveScale : 0.8, // Scale back down when the item is out of focus
],
Extrapolation.CLAMP
),
},
// Apply horizontal or vertical translation depending on the orientation
!data.vertical
? {
translateX: interpolate(
data.scrollX.value,
inputRange,
[200, 0, 200], // Move items to the sides when not centered
Extrapolation.CLAMP
),
}
: {
translateY: interpolate(
data.scrollX.value,
inputRange,
[200, 0, 200], // Move items vertically when not centered
Extrapolation.CLAMP
),
},
],
};
}, [data.scrollX, inputRange]);
// Return the animated style that will be applied to each item
return animatedStyle;
}
/**
* useLayoutDefaultAnimation creates a default animation for carousel items.
* It interpolates opacity and scaling based on the scroll position (scrollX).
* The default animation scales the items and adjusts their opacity when scrolling.
*/
export function useLayoutDefaultAnimation(data: ItemCarouselProps) {
// Calculate the input range based on the current index and direction (vertical/horizontal)
const inputRange = useMemo(() => {
const currentIndex = data.info.index;
return [
(currentIndex - 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // previous item position
currentIndex * (!data.vertical ? data.itemWidth : data.itemHeight), // current item position
(currentIndex + 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // next item position
];
}, [data.info.index, data.itemHeight, data.itemWidth, data.vertical]);
// Animated style that adjusts opacity and scale for the default animation
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(
data.scrollX.value, // The scroll position value used to interpolate
inputRange,
[0.8, 1, 0.8], // Decrease opacity when items are not in the center
Extrapolation.CLAMP // Prevent extrapolation beyond the defined range
),
transform: [
{
scale: interpolate(
data.scrollX.value,
inputRange,
[
data.inactiveScale ? data.inactiveScale : 0.8, // Scale down inactive items
1, // Scale to 1 when the item is centered
data.inactiveScale ? data.inactiveScale : 0.8, // Scale back down when the item is out of focus
],
Extrapolation.CLAMP
),
},
],
};
}, [data.scrollX, inputRange]);
// Return the default animated style
return animatedStyle;
}
/**
* useLayoutTinderAnimation creates a Tinder-style animation for carousel items.
* It includes a combination of opacity, scale, translation (X and Y), and rotation based on the scroll position (scrollX).
* This creates a dynamic "card-stack" effect with items rotating and translating as they approach or leave the center.
*/
export function useLayoutTinderAnimation(data: ItemCarouselProps) {
// Calculate the input range based on the current index and direction (vertical/horizontal)
const inputRange = useMemo(() => {
const currentIndex = data.info.index;
return [
(currentIndex - 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // previous item position
currentIndex * (!data.vertical ? data.itemWidth : data.itemHeight), // current item position
(currentIndex + 1) * (!data.vertical ? data.itemWidth : data.itemHeight), // next item position
];
}, [data.info.index, data.itemHeight, data.itemWidth, data.vertical]);
// Animated style that applies opacity, scale, translation, and rotation for the Tinder-like animation
const animatedStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(
data.scrollX.value, // The scroll position value used to interpolate
inputRange,
[0.8, 1, 0.8], // Decrease opacity when items are not in the center
Extrapolation.CLAMP // Prevent extrapolation beyond the defined range
),
transform: [
{
scale: interpolate(
data.scrollX.value,
inputRange,
[
data.inactiveScale ? data.inactiveScale : 0.8, // Scale down inactive items
1, // Scale to 1 when the item is centered
data.inactiveScale ? data.inactiveScale : 0.8, // Scale back down when the item is out of focus
],
Extrapolation.CLAMP
),
},
{
// Horizontal translation for Tinder-style effect
translateX: interpolate(
data.scrollX.value,
inputRange,
[100, 0, 100], // Move the item horizontally for the "stack" effect
Extrapolation.CLAMP
),
},
{
// Vertical translation for Tinder-style effect
translateY: interpolate(
data.scrollX.value,
inputRange,
[30, 0, 0], // Move the item vertically for the "stack" effect
Extrapolation.CLAMP
),
},
{
// Rotation for Tinder-style effect
rotate:
interpolate(
data.scrollX.value,
inputRange,
[22, 0, 0], // Rotate the item as it leaves the center
Extrapolation.CLAMP
) + 'deg', // Apply the degree of rotation
},
],
};
}, [data.scrollX, inputRange]);
// Return the animated style that creates the Tinder effect
return animatedStyle;
}