Search code examples
reactjsreact-nativecarouselreact-native-gesture-handler

How do I change the order of components in a react-native stack carousel?


I am trying to embed a stack carousel in my react-native application using react-native-reanimanted and the Gesture handler. The app is supposed to display a stack of components on top of each other and when the user swipes up, the component appears and sits on top of the previous component.

What instead happens when I run the simulator, is that when the user swipes up, the components that appears, sits behind the previous component instead of on top of it.

This is the code of the component that will be inside the container (I believe the problem lies here):

import React from 'react';
import {SafeAreaView, View} from 'react-native-safe-area-context';
import Stack from '../Stack/Stack';
import Animated, {
  useAnimatedStyle,
  interpolate,
  withTiming,
} from 'react-native-reanimated';
import {
  FlingGestureHandler,
  Directions,
  State,
} from 'react-native-gesture-handler';
import {StyleSheet} from 'react-native';

const Card = ({
  item,
  index,
  animatedValue,
  currentIndex,
  prevIndex,
  datalength,
  maxVisibleItems,
}) => {
  const animatedStyle = useAnimatedStyle(() => {
    const translateY = interpolate(
      animatedValue.value,
      [index - 1, index, index + 1],
      [-10, 1, 10],
    );
    const translateY2 = interpolate(
      animatedValue.value,
      [index - 1, index, index + 1],
      [-200, 1, 200],
    );
    const scale = interpolate(
      animatedValue.value,
      [index - 1, index, index + 1],
      [0.9, 1, 1.1],
    );
    const opacity = interpolate(
      animatedValue.value,
      [index - 1, index, index + 1],
      [1, 1, 0],
    );

    return {
      transform: [
        {
          translateY: index === prevIndex.value ? translateY2 : translateY,
        },
        {scale},
      ],
      opacity:
        index < currentIndex.value + maxVisibleItems - 1
          ? opacity
          : index === currentIndex.value + maxVisibleItems - 1
          ? withTiming(1)
          : withTiming(0),
    };
  });

  return (
    <FlingGestureHandler
      key="up"
      direction={Directions.UP}
      onHandlerStateChange={ev => {
        if (ev.nativeEvent.state === State.END) {
          if (currentIndex.value !== 0) {
            animatedValue.value = withTiming((currentIndex.value -= 1));
            prevIndex.value = currentIndex.value - 1;
          }
        }
      }}>
      <FlingGestureHandler
        key="down"
        direction={Directions.DOWN}
        onHandlerStateChange={ev => {
          if (ev.nativeEvent.state === State.END) {
            if (currentIndex.value !== datalength - 1) {
              animatedValue.value = withTiming((currentIndex.value += 1));
              prevIndex.value = currentIndex.value;
            }
          }
        }}>
        <Animated.View style={animatedStyle}>
          <Stack
            title={item.title}
            title1={item.title1}
            style={{zIndex: datalength - index}}
          />
        </Animated.View>
      </FlingGestureHandler>
    </FlingGestureHandler>
  );
};

export default Card;

const styles = StyleSheet.create({
  image: {
    position: 'absolute',
    borderRadius: 20,
  },
});

also the code for the container:

import {StyleSheet, Text, View} from 'react-native';
import React from 'react';
import Stack from '../Stack/Stack';
import {useSharedValue} from 'react-native-reanimated';
import Card from '../Card/Card';

const CardContainer = ({data, maxVisibleItems}) => {
  const animatedValue = useSharedValue(0);
  const currentIndex = useSharedValue(0);
  const prevIndex = useSharedValue(0);
  return (
    <>
      {data.map((item, index) => {
        return (
            // or Stack 
          <Card
            maxVisibleItems={maxVisibleItems}
            key={index}
            item={item}
            index={index}
            animatedValue={animatedValue}
            currentIndex={currentIndex}
            prevIndex={prevIndex}
            datalength={data.length}
          />
        );
      })}
    </>
  );
};

export default CardContainer;

I tried playing with some of the values in translateY and translateY2 but as well as the indexes with no success.


Solution

  • Okay so I figured it out:

    I added a ZIndex feature: This is the Card component:

    import React from 'react';
    import Stack from '../Stack/Stack';
    import Animated, {
      useAnimatedStyle,
      interpolate,
      withTiming,
    } from 'react-native-reanimated';
    import {
      FlingGestureHandler,
      Directions,
      State,
    } from 'react-native-gesture-handler';
    
    const Card = ({
      item,
      index,
      animatedValue,
      currentIndex,
      prevIndex,
      datalength,
      maxVisibleItems,
    }) => {
      const animatedStyle = useAnimatedStyle(() => {
        const translateY = interpolate(
          animatedValue.value,
          [index - 1, index, index + 1],
          [-5, 1, 5],
        );
        const translateY2 = interpolate(
          animatedValue.value,
          [index - 1, index, index + 1],
          [-200, 1, 200],
        );
        const scale = interpolate(
          animatedValue.value,
          [index - 1, index, index + 1],
          [0.9, 1, 1.1],
        );
        const opacity = interpolate(
          animatedValue.value,
          [index - 1, index, index + 1],
          [1, 1, 0],
        );
    
        const zIndex = interpolate(
          animatedValue.value,
          [index - 1, index, index + 1],
          [1, 2, 3], 
        );
    //psej
        return {
          transform: [
            {
              translateY: index === prevIndex.value ? translateY2 : translateY,
            },
            {scale},
          ],
          opacity:
            index < currentIndex.value + maxVisibleItems - 1
              ? opacity
              : index === currentIndex.value + maxVisibleItems - 1
              ? withTiming(1)
              : withTiming(0),
          zIndex,
        };
      });
    
      return (
        <FlingGestureHandler
          key="up"
          direction={Directions.UP}
          onHandlerStateChange={ev => {
            if (ev.nativeEvent.state === State.END) {
              if (currentIndex.value !== 0) {
                animatedValue.value = withTiming((currentIndex.value -= 1));
                prevIndex.value = currentIndex.value - 1;
              }
            }
          }}>
          <FlingGestureHandler
            key="down"
            direction={Directions.DOWN}
            onHandlerStateChange={ev => {
              if (ev.nativeEvent.state === State.END) {
                if (currentIndex.value !== datalength - 1) {
                  animatedValue.value = withTiming((currentIndex.value += 1));
                  prevIndex.value = currentIndex.value;
                }
              }
            }}>
            <Animated.View style={animatedStyle}>
              <Stack
                title={item.title}
                title1={item.title1}
                style={{zIndex: datalength - index}}
              />
            </Animated.View>
          </FlingGestureHandler>
        </FlingGestureHandler>
      );
    };
    export default Card;