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.85k stars 329 forks source link

Performance Lag in Carousel Animations on Real Android Devices Compared to Simulator #660

Open defild9 opened 2 months ago

defild9 commented 2 months ago

Describe the bug On a real Android device, animations in the CarouselListItem component are noticeably slower compared to the Android simulator. This issue manifests as lag during image scrolling, item transitions, and gesture handling. The performance difference is more pronounced on real devices.

To Reproduce Steps to reproduce the behavior:

  1. Implement the following CarouselListItem component:

    const CarouselListItem = ({
      item,
      shareHandler,
      addToWishlistHandler,
      removeFromWishlistHandler,
      stared,
    }: CarouselItem) => {
      const [currentImageIndex, setCurrentImageIndex] = React.useState(0);
    
      const baseOptions = {
        vertical: false,
        width: PAGE_WIDTH * 0.9,
        height: 232,
      } as const;
    
      const images = item?.lot?.image_urls || [];
      const isSold = item.lot.is_sold;
    
      return images.length > 0 ? (
        <CarouselContainer>
          <Carousel
            {...baseOptions}
            data={images}
            windowSize={3}
            scrollAnimationDuration={200}
            onSnapToItem={(index) => setCurrentImageIndex(index)}
            pagingEnabled={true}
            onProgressChange={(_, absoluteProgress) => {
              setCurrentImageIndex(Math.round(absoluteProgress));
            }}
            panGestureHandlerProps={{
              activeOffsetX: [-10, 10],
            }}
            renderItem={({ item }) => (
              <Flexbox>
                <ImageBackground
                  style={{ flex: 1 }}
                  imageStyle={{ borderTopLeftRadius: 16, borderTopRightRadius: 16 }}
                  source={{ uri: item }}
                >
                  <Flexbox justifyContent={'space-between'} borderRadius={16} height={232}>
                    <Flexbox flexDirection={'column'}>
                      <TouchableOpacity
                        onPress={shareHandler}
                        style={{
                          padding: 15,
                          backgroundColor: '#0C0C0C80',
                          borderTopLeftRadius: 16,
                          borderBottomRightRadius: 8,
                        }}
                      >
                        <ShareIcon />
                      </TouchableOpacity>
                    </Flexbox>
                    <Flexbox flexDirection={'column'}>
                      <TouchableOpacity
                        disabled={isSold}
                        onPress={
                          stared
                            ? removeFromWishlistHandler
                            : addToWishlistHandler
                            ? addToWishlistHandler
                            : () => {}
                        }
                        style={{
                          padding: 15,
                          backgroundColor: '#0C0C0C80',
                          borderTopRightRadius: 16,
                          borderBottomLeftRadius: 8,
                        }}
                      >
                        {stared ? (
                          <StarFilledIcon color={'#fff'} />
                        ) : (
                          <StarIcon color={isSold ? '#a0a0a0' : '#fff'} />
                        )}
                      </TouchableOpacity>
                    </Flexbox>
                  </Flexbox>
                </ImageBackground>
              </Flexbox>
            )}
          />
    
          <Dots
            passiveDotHeight={8}
            passiveDotWidth={8}
            activeDotWidth={12}
            activeDotHeight={8}
            length={images.length}
            active={currentImageIndex}
            activeColor="#1B6BC8"
            passiveColor="#E8EBEF"
          />
        </CarouselContainer>
      ) : (
        <TouchableWithoutFeedback>
          <Flexbox>
            <ImageBackground
              style={{ flex: 1 }}
              imageStyle={{ borderTopLeftRadius: 16, borderTopRightRadius: 16 }}
              source={{ uri: item.lot.image_urls[0] ?? item.lot.thumb }}
            >
              <Flexbox justifyContent={'space-between'} borderRadius={16} height={232}>
                <Flexbox flexDirection={'column'}>
                  <TouchableOpacity
                    onPress={shareHandler}
                    style={{
                      padding: 15,
                      backgroundColor: '#0C0C0C80',
                      borderTopLeftRadius: 16,
                      borderBottomRightRadius: 8,
                    }}
                  >
                    <ShareIcon />
                  </TouchableOpacity>
                </Flexbox>
                <Flexbox flexDirection={'column'}>
                  <TouchableOpacity
                    disabled={isSold}
                    onPress={
                      stared
                        ? removeFromWishlistHandler
                        : addToWishlistHandler
                        ? addToWishlistHandler
                        : () => {}
                    }
                    style={{
                      padding: 15,
                      backgroundColor: '#0C0C0C80',
                      borderTopRightRadius: 16,
                      borderBottomLeftRadius: 8,
                    }}
                  >
                    {stared ? (
                      <StarFilledIcon color={'#fff'} />
                    ) : (
                      <StarIcon color={isSold ? '#a0a0a0' : '#fff'} />
                    )}
                  </TouchableOpacity>
                </Flexbox>
              </Flexbox>
            </ImageBackground>
          </Flexbox>
        </TouchableWithoutFeedback>
      );
    };
    
    export default CarouselListItem;
  2. Run the app on both a real Android device and an Android simulator.

  3. Scroll through the images in the carousel on both platforms.

  4. Observe the difference in animation performance.

Expected behavior Animations should be smooth and responsive on both real Android devices and Android simulators.

Screenshots N/A

Versions (please complete the following information):

Smartphone (please complete the following information):

Additional context The issue might be related to the processing power differences between simulators and real devices, or it could be due to the configuration of scrollAnimationDuration, panGestureHandlerProps, or the complexity of the components within the carousel. Testing on multiple devices and optimizing gesture handling and animations may help resolve this.

dosubot[bot] commented 2 months ago

Hey there, @defild9! I'm here to help you with any bugs, questions, or contributions you have in mind. Let's squash those bugs together! 🤖

To address the performance lag in the CarouselListItem component's animations on real Android devices, you can try the following steps:

  1. Update the react-native-reanimated-carousel Package: Ensure you are using the latest version of the react-native-reanimated-carousel package. The latest patch reduces the amount of work done when rendering data, which should help improve performance [1].

  2. Optimize Animation Style: Use the interpolate and Extrapolate functions from react-native-reanimated to ensure smoother transitions and less computational overhead. Here is an example of how you can define a custom animation style:

    import { Extrapolate, interpolate } from "react-native-reanimated";
    import Carousel, { TAnimationStyle } from "react-native-reanimated-carousel";
    
    const animationStyle: TAnimationStyle = (value: number) => {
     "worklet";
    
     const itemOffsetInput = new Array(sideItemCount * 2 + 1)
       .fill(null)
       .map((_, index) => index - sideItemCount);
    
     const itemOffset = interpolate(
       value,
       itemOffsetInput,
       itemOffsetInput.map((item) => {
         if (item < 0) {
           return (-itemSize + sideItemWidth) * Math.abs(item);
         } else if (item > 0) {
           return (itemSize - sideItemWidth) * (Math.abs(item) - 1);
         }
         return 0;
       }) as number[],
     );
    
     const translate = interpolate(value, [-1, 0, 1], [-itemSize, 0, itemSize]) + centerOffset - itemOffset;
    
     const width = interpolate(value, [-1, 0, 1], [sideItemWidth, itemSize, sideItemWidth], Extrapolate.CLAMP);
    
     return {
       transform: [
         {
           translateX: translate,
         },
       ],
       width,
       overflow: "hidden",
     };
    };
  3. Adjust windowSize and scrollAnimationDuration Props: The windowSize prop can be used to control how many items are rendered around the current item. Reducing this number can improve performance. The default value is 0, which means all items will respond to pan gesture events [2]. You can try setting it to a smaller value, such as 3, as you have done in your example.

    The scrollAnimationDuration prop controls the duration of the scroll animation. The default value is 500 ms [2]. You can experiment with different values to find the optimal duration for smooth animations.

  4. Review Similar Issues: There are similar issues reported in the repository that might provide additional insights:

By following these steps, you should be able to mitigate the performance issues you're experiencing on real Android devices.

To continue talking to Dosu, mention @dosu.

jun-jaehyuk-lee commented 1 month ago

same issue

HelgiMagic commented 1 month ago

same