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.64k stars 303 forks source link

Screen rotation issue #327

Open Danielcomes92 opened 1 year ago

Danielcomes92 commented 1 year ago

Hi!, Firstly, thank you for the library, it works very smoothly and has a great performance.

The only thing right now is this, I'm working on a tablet with screen rotation and when I rotate it the carousel item should be centered but it isn't, it's like the width wasn't been calculated again and therefore the slide position is wrong.

Could someone give me some tips or advice? I tried with the bare example and it is happening as well.

Simulator Screen Shot - iPad mini (6th generation) - 2022-12-27 at 10 44 49

Simulator Screen Shot - iPad mini (6th generation) - 2022-12-27 at 10 44 55

Thank you!

github-actions[bot] commented 1 year ago

🔴

👨‍💻 I'm so busy recently, so I'm going away for a little while, but I'll come back by the end of this month.

♥️ Rest assured, I love this project, I will not give up.

2022.11.xx


dohooo commented 1 year ago

Hi, You can put width to the "key" prop. The carousel will be re-generated when the width props change. I think this approach could work as a temporary workaround.

Danielcomes92 commented 1 year ago

Hi, thanks for getting back to me so quickly.

I'll show you how I did it because I think there's some memory there. The width variable is passed expecting a re-render in the carousel and therefore centering the item, but it doesn't work. Here's my code;

const TabletCarousel = ({ ENTRIES }: TabletCarouselProps) => {
  const { width, height } = useWindowDimensions();

  const [activeSliderIndex, setActiveSliderIndex] = useState(0);

  return (
    <Carousel
      loop={false}
      width={width}
      height={height * 0.75}
      data={ENTRIES}
      style={{ flex: 1 }}
      renderItem={({ index }) => (
        <View
          style={{
            backgroundColor: 'blue',
            flex: 1,
          }}
        >
          <Text
            style={{
              textAlign: 'center',
              fontSize: 30,
            }}
          >
            {index}
          </Text>
        </View>
      )}
      onSnapToItem={(index) => setActiveSliderIndex(index)}
    />
  );
};

Once again, thank you and I'm looking forward to hearing from you.

bruteforks commented 1 year ago

Wondering about this same issue myself. Any progress on this? Especially for elements that take up the entire height and width of the screen; which imo should be a common use case.

@Danielcomes92 did you ever solve this issue?

bruteforks commented 1 year ago

Here's what I came up with. haven't really done any testing, but it works kinda. Only bug I see is screen flashes old layout for a second when rotating screen. Will update if I make further improvements

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

function ResponsiveCarousel() {
  const [screenDimensions, setScreenDimensions] = useState(
    Dimensions.get('window'),
  );

  useEffect(() => {
    const handleDimensionChange = newDimensions => {
      setScreenDimensions(newDimensions.window);
    };

    Dimensions.addEventListener('change', handleDimensionChange);
    return () => {
      Dimensions.removeEventListener('change', handleDimensionChange);
    };
  }, []);

  const data = [
    {
      id: 1,
      text: 'Item 1',
    },
    {
      id: 2,
      text: 'Item 2',
    },
    // ...
  ];

  return (
    <View style={{flex: 1}}>
      <Carousel
        loop
        width={screenDimensions.width}
        height={screenDimensions.height}
        data={data}
        scrollAnimationDuration={1000}
        onSnapToItem={index => console.log('current index:', index)}
        renderItem={({index}) => (
          <View
            style={{
              flex: 1,
              borderWidth: 1,
              justifyContent: 'center',
            }}>
            <Text style={{textAlign: 'center', fontSize: 30}}>
              {data[index].text}
            </Text>
          </View>
        )}
      />
    </View>
  );
}

export default ResponsiveCarousel;
bruteforks commented 1 year ago

this fixes the layout bug on rotation. Although I'm kinda new to react, so not sure if this is badly implemented or not. The carouselKey does a calculation on rotation and re-renders the carousel.

import React, {useState, useEffect} from 'react';
import {View, Dimensions, Text, ImageBackground, StatusBar} from 'react-native';
import Carousel from 'react-native-reanimated-carousel';

const carousel_0 = require('../assets/carousel_0.jpg');
const carousel_1 = require('../assets/carousel_1.jpg');
const carousel_2 = require('../assets/carousel_2.jpg');

function ResponsiveCarousel() {
  const [screenDimensions, setScreenDimensions] = useState(
    Dimensions.get('window'),
  );

  useEffect(() => {
    const handleDimensionChange = ({window: newDimensions}) => {
      setScreenDimensions(newDimensions);
    };

    Dimensions.addEventListener('change', handleDimensionChange);
    return () => {
      Dimensions.removeEventListener('change', handleDimensionChange);
    };
  }, []);

  const data = [
    {
      id: 1,
      text: 'Item 1',
      image: carousel_0,
    },
    {
      id: 2,
      text: 'Item 2',
      image: carousel_1,
    },
    {
      id: 3,
      text: 'Item 3',
      image: carousel_2,
    },
  ];

  const carouselKey = `${screenDimensions.width}-${screenDimensions.height}`;

  return (
    <View style={{flex: 1}}>
      <StatusBar hidden />
      <Carousel
        key={carouselKey}
        loop
        width={screenDimensions.width}
        height={screenDimensions.height}
        data={data.map(item => item.id)}
        scrollAnimationDuration={1000}
        onSnapToItem={index => console.log('current index:', index)}
        renderItem={({index}) => (
          <ImageBackground
            source={data[index].image}
            resizeMode="cover"
            style={{
              flex: 1,
              justifyContent: 'center',
            }}>
            <Text style={{textAlign: 'center', fontSize: 30, color: 'white'}}>
              {data[index].text}
            </Text>
          </ImageBackground>
        )}
      />
    </View>
  );
}

export default ResponsiveCarousel;
KaterynaPotrusRedge commented 6 months ago

Hi, I have the same issue here. Adding width to the key prop works kinda buggy. Is there any chance to have this problem fixed?

alany411 commented 5 months ago

I was able to get the carousel to rerender and scroll to the active index with a mixture of useRef, useEffect, and key. Here's a very minimal code snippet:

const SomeCarousel = () => {
  const { width, height } = useWindowDimensions();
  const carouselRef = useRef(null);
  const activeSlideIndex = useRef(0);

  useEffect(() => {
    carouselRef.current?.scrollTo({
      index: activeSlideIndex.current,
      animated: false,
    })
  }, [width])

  return (
    <Carousel
      key={width}
      ref={carouselRef}
      onSnapToItem={(index) => { activeSlideIndex.current = index }}
     // ...all your other props
    />
  );
};