Open
Description
Describe the bug
When using the carousel with infinite loop and autoplay enabled, the behavior is inconsistent for lists with 3 items. Specifically, the first item is not rendered correctly when reaching the end of the list, causing a noticeable delay and breaking the expected smooth transition. This issue does not occur with lists of 2 or 4 items, which work as expected.
Expected behavior
The carousel should smoothly transition from the last item back to the first item without any delay or rendering issues, just as it does with lists of 2 or 4 items.
Screenshots/Videos
I will link three videos in the issue:
- One showing the correct behavior with 2 items.
2.items.webm
- One showing the correct behavior with 4 items.
4.items.webm
- One showing the incorrect behavior with 3 items.
3.items.webm
Versions
- react: v18.3.1
- react-native: v0.76.7
- react-native-reanimated: v3.17.1
- react-native-reanimated-carousel: v4.0.2
- react-native-gesture-handler: v2.24.0
Smartphone
- Device: Pixel 8a API 35 (Android Emulator)
- OS: Android
- Browser: N/A (React Native)
- Version: N/A
Additional context
- My development environment is Windows 11.
- The project uses Expo SDK 52.
Temporary Workaround
- I duplicated the data when the list length is 3, but this feels like a hack and not a proper solution. It would be great to have a native fix for this issue.
Code to reproduce
import ReanimatedCarousel from "react-native-reanimated-carousel";
import { StyleProp, ViewStyle, Dimensions } from "react-native";
import { useEffect, useState } from "react";
const { width } = Dimensions.get("window");
interface CarouselProps<T> {
data: T[];
renderItem: (item: T, index: number) => JSX.Element;
containerStyle?: StyleProp<ViewStyle>;
carouselWindowHeight: number;
carouselWindowWidth?: number;
carouselItemWidth: number;
marginBetweenItems?: number;
infiniteLoopEnabled?: boolean;
pagingEnabled?: boolean;
snapEnabled?: boolean;
autoPlayEnabled?: boolean;
autoPlayInterval?: number;
}
export default function Carousel<T>({
data,
renderItem,
containerStyle,
carouselWindowHeight,
carouselWindowWidth = width,
carouselItemWidth,
marginBetweenItems = 0,
infiniteLoopEnabled = false,
pagingEnabled = true,
snapEnabled = true,
autoPlayEnabled = false,
autoPlayInterval = 2000,
}: CarouselProps<T>) {
const getParallaxScrollingOffset =
carouselWindowWidth - carouselItemWidth - marginBetweenItems;
const [modifiedData, setModifiedData] = useState<T[]>(data);
useEffect(() => {
if (data.length === 3) {
setModifiedData([...data, ...data]);
}
}, [data]);
if (data.length <= 1) {
return <></>;
}
return (
<ReanimatedCarousel
testID="reanimatedCarousel"
width={carouselWindowWidth}
height={carouselWindowHeight}
pagingEnabled={pagingEnabled}
snapEnabled={snapEnabled}
autoPlay={autoPlayEnabled}
loop={infiniteLoopEnabled}
autoPlayInterval={autoPlayInterval}
containerStyle={containerStyle}
mode="parallax"
modeConfig={{
parallaxScrollingScale: 1,
parallaxScrollingOffset: getParallaxScrollingOffset,
}}
onConfigurePanGesture={(panGestureHandlerProps) => {
panGestureHandlerProps.activeOffsetX([-10, 10]);
}}
data={modifiedData}
renderItem={({ item, index }) => renderItem(item, index)}
/>
);
}
To Reproduce
Steps to reproduce the behavior:
- Set up a carousel with infinite loop and autoplay enabled.
- Use a data list with exactly 3 items.
- Observe the carousel behavior when it reaches the end of the list.
- Notice the delay and incorrect rendering of the first item.