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?
CodePudding user response:
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: {},
});
CodePudding user response:
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.
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',
}