skip to Main Content

What I’m building:

I’m creating a card that will render with a question mark at top of it and when the user press it, the card will flip with animation and show some information.

Code:

From a code perspective it looks like this:

import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import { GlobalStyles } from '../../constants/styles';
import { useTranslation } from 'react-i18next';

export default Card = ({ isSpy, guessingItem, onPress }) => {
  const { t } = useTranslation();

  let currentValue = 0;
  const animatedValue = new Animated.Value(0);
  animatedValue.addListener((v) => (currentValue = v.value));

  const frontInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['0deg', '180deg'],
  });

  const backInterpolate = animatedValue.interpolate({
    inputRange: [0, 180],
    outputRange: ['180deg', '360deg'],
  });

  const frontAnimatedStyles = {
    transform: [{ rotateY: frontInterpolate }],
  };

  const backAnimatedStyles = {
    transform: [{ rotateY: backInterpolate }],
  };

  const flipCard = () => {
    console.log(currentValue);
    if (currentValue >= 90) {
      Animated.spring(animatedValue, {
        toValue: 0,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
      setTimeout(() => onPress(), 600);
    } else {
      Animated.spring(animatedValue, {
        toValue: 180,
        tension: 10,
        friction: 8,
        useNativeDriver: false,
      }).start();
    }
  };

  return (
    <View style={styles.constainer}>
      <Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
        <View style={GlobalStyles.styles.flexbox}>
          <Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
            <FontAwesome name="question" size={160} />
          </Animated.View>
        </View>
      </Pressable>
      <Pressable onPress={flipCard}>
        <View style={GlobalStyles.styles.flexbox}>
          <Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
            <View style={styles.constainer}>
              {!isSpy && (
                <>
                  <FontAwesome name={guessingItem.section.icon} size={60} />
                  <Text style={styles.itemName}>{guessingItem.name}</Text>
                </>
              )}
              {isSpy && (
                <>
                  <FontAwesome name="user-secret" size={60} />
                  <Text style={styles.placeName}>{t('youAreSpy')}</Text>
                </>
              )}
            </View>
          </Animated.View>
        </View>
      </Pressable>
    </View>
  );
};

const styles = StyleSheet.create({
  constainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  innerContainer: {
    height: 300,
    width: 230,
    marginVertical: 50,
    padding: 60,
    borderWidth: 6,
    borderColor: GlobalStyles.colors.primary50,
    borderRadius: 20,
    justifyContent: 'center',
    alignItems: 'center',
    backfaceVisibility: 'hidden',
  },
  innerContainerBack: {
    position: 'absolute',
    right: -115,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
  },
  itemName: {
    marginTop: 20,
    fontSize: 18,
    alignItems: 'center',
    color: GlobalStyles.colors.primary50,
  },
  pressed: {},
});

What is an issue:


On-screen below you can see a pressable element with red background.
If I click on any part of this red part, the card would flip. If I test it on Android, clicking on anything inside the white border would not trigger a flip. Only clicking outside of that white border but still inside the red background would trigger a flip.

The question is: Why it behaves differently when I am using basic react-native elements? How can I fix that so clicking would always work for the inside click?
enter image description here

2

Answers


  1. I tested this component on my side and it works well. I think there is no problem with the Card component. Please check the parent component of the Card.
    enter image description here

    I only changed "right" value from -115 to 0 in the style of innerContainerBack.

      innerContainerBack: {
        position: 'absolute',
        right: 0,
        bottom: 0,
        justifyContent: 'center',
        alignItems: 'center',
      }
    
    Login or Signup to reply.
  2. And you’d better use only one Pressable Component and remove the View component in the Pressable. So my ultimate solution is

    import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
    import { FontAwesome } from '@expo/vector-icons';
    import { GlobalStyles } from '../../constants/styles';
    import { useTranslation } from 'react-i18next';
    
    export default Card = ({ isSpy, guessingItem, onPress }) => {
      const { t } = useTranslation();
    
      let currentValue = 0;
      const animatedValue = new Animated.Value(0);
      animatedValue.addListener((v) => (currentValue = v.value));
    
      const frontInterpolate = animatedValue.interpolate({
        inputRange: [0, 180],
        outputRange: ['0deg', '180deg'],
      });
    
      const backInterpolate = animatedValue.interpolate({
        inputRange: [0, 180],
        outputRange: ['180deg', '360deg'],
      });
    
      const frontAnimatedStyles = {
        transform: [{ rotateY: frontInterpolate }],
      };
    
      const backAnimatedStyles = {
        transform: [{ rotateY: backInterpolate }],
      };
    
      const flipCard = () => {
        console.log(currentValue);
        if (currentValue >= 90) {
          Animated.spring(animatedValue, {
            toValue: 0,
            tension: 10,
            friction: 8,
            useNativeDriver: false,
          }).start();
          setTimeout(() => onPress(), 600);
        } else {
          Animated.spring(animatedValue, {
            toValue: 180,
            tension: 10,
            friction: 8,
            useNativeDriver: false,
          }).start();
        }
      };
    
      return (
        <View style={styles.constainer}>
          <Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
              <Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
                <FontAwesome name="question" size={160} />
              </Animated.View>
              <Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
                <View style={styles.constainer}>
                  {!isSpy && (
                    <>
                      <FontAwesome name={guessingItem.section.icon} size={60} />
                      <Text style={styles.itemName}>{guessingItem.name}</Text>
                    </>
                  )}
                  {isSpy && (
                    <>
                      <FontAwesome name="user-secret" size={60} />
                      <Text style={styles.placeName}>{t('youAreSpy')}</Text>
                    </>
                  )}
                </View>
              </Animated.View>
          </Pressable>
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      constainer: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
      },
      innerContainer: {
        height: 300,
        width: 230,
        marginVertical: 50,
        padding: 60,
        borderWidth: 6,
        borderColor: GlobalStyles.colors.primary50,
        borderRadius: 20,
        justifyContent: 'center',
        alignItems: 'center',
        backfaceVisibility: 'hidden',
      },
      innerContainerBack: {
        position: 'absolute',
        right: 0,
        bottom: 0,
        justifyContent: 'center',
        alignItems: 'center',
      },
      itemName: {
        marginTop: 20,
        fontSize: 18,
        alignItems: 'center',
        color: GlobalStyles.colors.primary50,
      },
      pressed: {},
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search