dohooo / react-native-reanimated-carousel

🎠 React Native swiper/carousel component, fully implemented using reanimated v2, support to iOS/Android/Web. (Swiper/Carousel)
https://react-native-reanimated-carousel.vercel.app
MIT License
2.72k stars 313 forks source link

cannot scroll horizontal #664

Open sohuichoi opened 1 week ago

sohuichoi commented 1 week ago

Describe the bug I set the vertical-stack carousel which is swipped by horizontal gesture. But in the real device, the carousel cannot scroll horizontally well, like the attached file. To Reproduce Steps to reproduce the behavior: the following code is what I develop.

import React from 'react';
import { Text, View } from 'react-native';
import Carousel from 'react-native-reanimated-carousel';

const CustomCarousel = () => {
    const [currentImageIndex, setCurrentImageIndex] = React.useState(0);
    const data = [1, 2, 3, 4, 5];
    const baseOptions = {
        // vertical: false,
        width: 300,
        height: 232,
    } as const;

    return (
        // <CarouselContainer>
        <Carousel
            {...baseOptions}
            style={{
                width: 400,
                height: 400,
                // display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                borderWidth: 1,
            }}
            data={[1, 2, 3, 4, 5].reverse()}
            defaultIndex={4} // data의 λ§ˆμ§€λ§‰ 인덱슀둜 μ‹œμž‘ μ„ΈνŒ…ν•΄μ•Όν•¨
            windowSize={3}
            scrollAnimationDuration={500} // μ• λ‹ˆλ©”μ΄μ…˜ 지속 μ‹œκ°„μ„ 늘림 (ms λ‹¨μœ„)
            onSnapToItem={(index) => setCurrentImageIndex(index)}
            pagingEnabled={true}
            onProgressChange={(_, absoluteProgress) => {
                setCurrentImageIndex(Math.round(absoluteProgress));
            }}
            onConfigurePanGesture={(panGesture) => {
                panGesture.activeOffsetX([-50, 50]); // μž„κ³„κ°’μ„ 늘림
            }}
            panGestureHandlerProps={{
                activeOffsetX: [-50, 50], // μŠ€μ™€μ΄ν”„ μž„κ³„κ°’ μ„€μ •
            }}
            loop
            mode='vertical-stack'
            modeConfig={{
                snapDirection: 'right',
                stackInterval: -30,
                scaleInterval: 0.1,
                rotateZDeg: 5,
            }}
            customConfig={() => {
                viewCount: 1;
            }}
            renderItem={({ item, index }) => {
                const colors = ['#FFB3BA', '#BAFFC9', '#BAE1FF', '#FFFFBA', '#FFD8B1'];
                const backgroundColor = colors[index % colors.length];

                return (
                    <View
                        style={{
                            height: 320,
                            width: 320,
                            backgroundColor,
                            display: 'flex',
                            justifyContent: 'center',
                            alignItems: 'center',
                        }}
                    >
                        <Text
                            style={{
                                fontSize: 40,
                            }}
                        >
                            {item}
                        </Text>
                    </View>
                );
            }}
        />

        // </CarouselContainer>
    );
};

export default CustomCarousel;

Expected behavior A clear and concise description of what you expected to happen. I want the scroll animation works well. Screenshots If applicable, add screenshots to help explain your problem.

https://github.com/user-attachments/assets/537f624f-a1e2-4eed-8dc8-0f60913bdc2f

Versions (please complete the following information):

Smartphone (please complete the following information):

Additional context Add any other context about the problem here.

dosubot[bot] commented 1 week ago

Your issue with the react-native-reanimated-carousel not scrolling horizontally on a real device when using the vertical-stack mode can be resolved by using the fixedDirection API. This API ensures that the carousel scrolls in a consistent direction.

Here's how you can modify your code to use the fixedDirection API:

import React from 'react';
import { Text, View } from 'react-native';
import Carousel from 'react-native-reanimated-carousel';

const CustomCarousel = () => {
    const [currentImageIndex, setCurrentImageIndex] = React.useState(0);
    const data = [1, 2, 3, 4, 5];
    const baseOptions = {
        width: 300,
        height: 232,
    } as const;

    return (
        <Carousel
            {...baseOptions}
            style={{
                width: 400,
                height: 400,
                justifyContent: 'center',
                alignItems: 'center',
                borderWidth: 1,
            }}
            data={[1, 2, 3, 4, 5].reverse()}
            defaultIndex={4}
            windowSize={3}
            scrollAnimationDuration={500}
            onSnapToItem={(index) => setCurrentImageIndex(index)}
            pagingEnabled={true}
            onProgressChange={(_, absoluteProgress) => {
                setCurrentImageIndex(Math.round(absoluteProgress));
            }}
            onConfigurePanGesture={(panGesture) => {
                panGesture.activeOffsetX([-50, 50]);
            }}
            loop
            mode='vertical-stack'
            modeConfig={{
                snapDirection: 'right',
                stackInterval: -30,
                scaleInterval: 0.1,
                rotateZDeg: 5,
            }}
            fixedDirection="horizontal" // Add this line
            renderItem={({ item, index }) => {
                const colors = ['#FFB3BA', '#BAFFC9', '#BAE1FF', '#FFFFBA', '#FFD8B1'];
                const backgroundColor = colors[index % colors.length];

                return (
                    <View
                        style={{
                            height: 320,
                            width: 320,
                            backgroundColor,
                            justifyContent: 'center',
                            alignItems: 'center',
                        }}
                    >
                        <Text
                            style={{
                                fontSize: 40,
                            }}
                        >
                            {item}
                        </Text>
                    </View>
                );
            }}
        />
    );
};

export default CustomCarousel;

This should ensure that the carousel scrolls horizontally even when using the vertical-stack mode [1].

To continue talking to Dosu, mention @dosu.

sohuichoi commented 1 week ago

@dosu It still doesn't work. When I swipe horizontally, the View scroll down sometimes.

dosubot[bot] commented 1 week ago

To fix the issue where the react-native-reanimated-carousel in vertical-stack mode scrolls down sometimes when swiped horizontally, even after using the fixedDirection API, you need to ensure that the fixedDirection is correctly applied during the gesture updates and end events.

Here is the relevant part of the code where fixedDirection is applied:

const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
  "worklet";

  if (validStart.value) {
    validStart.value = false;
    cancelAnimation(translation);
  }
  touching.value = true;
  const { translationX, translationY } = e;

  let panTranslation = isHorizontal.value
    ? translationX
    : translationY;

  if (fixedDirection === "negative")
    panTranslation = -Math.abs(panTranslation);

  else if (fixedDirection === "positive")
    panTranslation = +Math.abs(panTranslation);

  if (!loop) {
    if ((translation.value > 0 || translation.value < -max.value)) {
      const boundary = translation.value > 0 ? 0 : -max.value;
      const fixed = boundary - panOffset.value;
      const dynamic = panTranslation - fixed;
      translation.value = boundary + dynamic * 0.5;
      return;
    }
  }

  const translationValue = panOffset.value + panTranslation;
  translation.value = translationValue;
}, [
  isHorizontal,
  max,
  panOffset,
  loop,
  overscrollEnabled,
  fixedDirection,
  translation,
  validStart,
  touching,
]);

const onGestureEnd = useCallback((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>, _success: boolean) => {
  "worklet";
  const { velocityX, velocityY, translationX, translationY } = e;
  const scrollEndVelocityValue = isHorizontal.value
    ? velocityX
    : velocityY;
  scrollEndVelocity.value = scrollEndVelocityValue;

  let panTranslation = isHorizontal.value
    ? translationX
    : translationY;

  if (fixedDirection === "negative")
    panTranslation = -Math.abs(panTranslation);

  else if (fixedDirection === "positive")
    panTranslation = +Math.abs(panTranslation);

  scrollEndTranslation.value = panTranslation;

  const totalTranslation = scrollEndVelocityValue + panTranslation;

  if (
    maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe
  ) {
    const nextPage = Math.round((panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
    translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
  }
  else if (
    minScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) < minScrollDistancePerSwipe
  ) {
    const nextPage = Math.round((panOffset.value + minScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
    translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
  }
  else {
    endWithSpring(panTranslation, scrollEndVelocityValue, onScrollEnd);
  }

  if (!loop)
    touching.value = false;
}, [
  size,
  loop,
  touching,
  panOffset,
  translation,
  isHorizontal,
  scrollEndVelocity,
  scrollEndTranslation,
  fixedDirection,
  maxScrollDistancePerSwipeIsSet,
  maxScrollDistancePerSwipe,
  minScrollDistancePerSwipe,
  endWithSpring,
  withSpring,
  onScrollEnd,
]);

Ensure that the fixedDirection logic is correctly applied and that the translation value is being updated appropriately based on the direction constraints. If the issue persists, you may need to debug further to ensure that the fixedDirection is being respected throughout the gesture handling process [1].

To continue talking to Dosu, mention @dosu.