How to remove the first card from PanResponder by using functional react-native?

346 Views Asked by At

I'm learning the react-native and yes I'm beginner, I want to make similar like Tinder apps, but I have met a problem which is cannot swipe the first card(mean cannot remove the first card). There has no error message for these codes. I have tried few of solutions but still cannot work too. Please somebody help me for these codes.

import React, { useEffect,useState,useRef } from 'react';
import { StyleSheet, Text, View, Dimensions, Image, Animated, PanResponder, Platform } from 'react-native';

const SCREEN_HEIGHT = Dimensions.get('window').height;
const SCREEN_WIDTH = Dimensions.get('window').width;
import Icon from 'react-native-vector-icons/Ionicons';
const Users = [
  { id: "1", uri: require('../image/antman.jpg') },
  { id: "2", uri: require('../image/butterfly.jpg') },
  { id: "3", uri: require('../image/captainmarvel.jpg') },
  { id: "4", uri: require('../image/antman.jpg') },
  { id: "5", uri: require('../image/antman.jpg') },
]

const colors = [
  {
    id: 1,
    color: 'red',
  },
  {
    id: 2,
    color: 'green'
  },
  {
    id: 3,
    color: 'blue',
  }
]

const SignUpScreen = () =>{

    const [position, setPosition] = useState(new Animated.ValueXY());
    const [currentIndex,setCurrentIndex] =useState(0);

    const rotate = position.x.interpolate({
      inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
      outputRange: ['-30deg', '0deg', '10deg'],
      extrapolate: 'clamp'
    })

    const rotateAndTranslate = {
      transform: [{
        rotate: rotate
      },
      ...position.getTranslateTransform()
      ]
    }

    const likeOpacity = position.x.interpolate({
      inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
      outputRange: [0, 0, 1],
      extrapolate: 'clamp'
    })
    const dislikeOpacity = position.x.interpolate({
      inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
      outputRange: [1, 0, 0],
      extrapolate: 'clamp'
    })

    const nextCardOpacity = position.x.interpolate({
      inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
      outputRange: [1, 0, 1],
      extrapolate: 'clamp'
    })
    const nextCardScale = position.x.interpolate({
      inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
      outputRange: [1, 0.8, 1],
      extrapolate: 'clamp'
    })


 
    const panResponder = PanResponder.create({

      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onPanResponderMove: (evt, gestureState) => {

        setPosition(new Animated.ValueXY({x: gestureState.dx, y: gestureState.dy}));
      },
      onPanResponderRelease: (evt, gestureState) => {

        if (gestureState.dx > 120) {
          Animated.spring(position, {
            toValue: { x: SCREEN_WIDTH + 100, y: gestureState.dy }//,
            //duration: 500
          }).start(() => {
            //setCurrentIndex(currentIndex + 1 ), 
            setPosition(new AnimatedValue({x: 0, y: 0 }));  
            })
          }
        else if (gestureState.dx < -120) {
          Animated.spring(position, {
            toValue: { x: -SCREEN_WIDTH - 100, y: gestureState.dy }//,
            //duration: 500
          }).start(() => {
            //setCurrentIndex(currentIndex + 1 ), 
            setPosition(new AnimatedValue({x: 0, y: 0 }));  
            })
        }
        else {
          Animated.spring(position, {
            toValue: { x: 0, y: 0 },
            duration: 500
            //friction: 4
          }).start()
        }
      }
    })
  


  renderUsers = () => {

    return Users.map((item, i) => {

      
      if (i < currentIndex) {
        return null;
      }
      else if (i == currentIndex) {

        return (
          <Animated.View
            {...panResponder.panHandlers}
            key={item.id} style={[rotateAndTranslate, { height: SCREEN_HEIGHT - 120, width: SCREEN_WIDTH, padding: 10, position: 'absolute' }]}>
            <Animated.View style={{ opacity: likeOpacity, transform: [{ rotate: '-30deg' }], position: 'absolute', top: 50, left: 40, zIndex: 1000 }}>
              <Text style={{ borderWidth: 1, borderColor: 'green', color: 'green', fontSize: 32, fontWeight: '800', padding: 10 }}>LIKE</Text>

            </Animated.View>

            <Animated.View style={{ opacity: dislikeOpacity, transform: [{ rotate: '30deg' }], position: 'absolute', top: 50, right: 40, zIndex: 1000 }}>
              <Text style={{ borderWidth: 1, borderColor: 'red', color: 'red', fontSize: 32, fontWeight: '800', padding: 10 }}>NOPE</Text>

            </Animated.View>

            <Image
              style={{ flex: 1, height: null, width: null, resizeMode: 'cover', borderRadius: 20 }}
              source={item.uri} />

          </Animated.View>
        )
      }
      else {
        return (
          <Animated.View

            key={item.id} style={[{
              opacity: nextCardOpacity,
              transform: [{ scale: nextCardScale }],
              height: SCREEN_HEIGHT - 120, width: SCREEN_WIDTH, padding: 10, position: 'absolute'
            }]}>
            <Animated.View style={{ opacity: 0, transform: [{ rotate: '-30deg' }], position: 'absolute', top: 50, left: 40, zIndex: 1000 }}>
              <Text style={{ borderWidth: 1, borderColor: 'green', color: 'green', fontSize: 32, fontWeight: '800', padding: 10 }}>LIKE</Text>

            </Animated.View>

            <Animated.View style={{ opacity: 0, transform: [{ rotate: '30deg' }], position: 'absolute', top: 50, right: 40, zIndex: 1000 }}>
              <Text style={{ borderWidth: 1, borderColor: 'red', color: 'red', fontSize: 32, fontWeight: '800', padding: 10 }}>NOPE</Text>

            </Animated.View>

            <Image
              style={{ flex: 1, height: null, width: null, resizeMode: 'cover', borderRadius: 20 }}
              source={item.uri} />

          </Animated.View>
        )
      }
    }).reverse()
  }

  
    return (
      <View style={{ flex: 1 }}>
        <View style={{ height: 10 }}>

        </View>
        <View style={{ flex: 1 }}>
          {renderUsers()}
        </View>
        <View style={{ height: 20 }}>

        </View>


      </View>

    );
  };

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});



export default SignUpScreen;

These are all functional react native. Please help me solve the problems thank you very much.

2

There are 2 best solutions below

0
On

Try the following, addition with @Kangkan Lahkar solution,

position.setValue({x: gestureState.dx, y: gestureState.dy});
//or 
position.setValue({x: 0, y: 0});

separate this into 2 separate transform:

const rotateAndTranslate = {
  transform: [{
    rotate: rotate
  },
  ...position.getTranslateTransform()
  ]
}

and apply to 2 different style container in the same panResponder eg:

<Animated.View {...panResponder.panHandlers}
   style={[...position.getTranslateTransform()]}>

   <Animated.View style={{ ... transform:[{rotate: rotate}] }}> ....</Animated.View> 

</Animated.View>
1
On

Here is the issue.

const [position, setPosition] = useState(new Animated.ValueXY());

Rather use it

const [position] = useState(new Animated.ValueXY());

Don't use setPosition() to update position.

setPosition(new Animated.ValueXY({x: gestureState.dx, y: gestureState.dy}));
//or
setPosition(new AnimatedValue({x: 0, y: 0 })); 

Try setValue() to update position

position.setValue({x: gestureState.dx, y: gestureState.dy});
//or
position.setValue({x: 0, y: 0});

I did the same, it worked