I’ve written a react component that is supposed to distinguish between touches and swipes. The idea is:
- If the user swipes on the component, its image changes. This is working fine.
- If the user touches the component, it shrinks whilst being touched and expands when released. This is also working fine.
- It must only call “onTouch” at the end of a touch gesture, but NEVER when a swipe takes place. This is because I only want the user go the new page when they have pressed the element, not swiped across it. Think of Airbnb and when the user is swiping along an Explore card vs selecting it. This is the behaviour I want.
I can’t get Requirement 3 to work, because it is always calling onTouch when a swipe occurs. This occurs when holding, and then swiping whilst being held, or if a quick swipe takes place. But neither of these should trigger onTouch. Please advise how I can fix this? I’ve tried asking ChatGPT but it fails every time!
import React, { useRef, useState } from "react";
import {
Animated,
View,
TouchableWithoutFeedback,
StyleSheet,
} from "react-native";
interface Props {
children: React.ReactNode;
duration?: number; // Optional prop to control the duration of the animation
onTouch: () => void;
}
function ShrinkOnTouch({ children, duration, onTouch }: Props) {
if (duration === undefined) duration = 125;
const scale = useRef(new Animated.Value(1)).current; // Initial scale value (1)
const [isTouched, setIsTouched] = useState(false);
// Function to handle the shrink action
const handleTouchStart = () => {
setIsTouched(true);
Animated.timing(scale, {
toValue: 0.95, // Shrink to 90% of the original size
duration: duration, // Set the duration of the shrinking animation
useNativeDriver: true,
}).start();
};
// Function to handle the release action (expand back to original size)
const handleTouchEnd = () => {
if (isTouched) {
setIsTouched(false);
Animated.timing(scale, {
toValue: 1, // Expand back to original size
duration: duration, // Set the duration of the expanding animation
useNativeDriver: true,
}).start();
onTouch();
}
};
// Function to handle swipe detection (expand back to original size if swipe detected)
const handleTouchMove = (e: any) => {
if (
isTouched &&
(Math.abs(e.nativeEvent.pageX) > 5 || Math.abs(e.nativeEvent.pageY) > 5)
) {
// If swipe detected, expand the component back to original size
setIsTouched(false);
Animated.timing(scale, {
toValue: 1,
duration: duration, // Same duration for the swipe expansion
useNativeDriver: true,
}).start();
}
};
return (
<View style={styles.container}>
<TouchableWithoutFeedback
onPressIn={handleTouchStart}
onPressOut={handleTouchEnd}
onPress={handleTouchEnd}
>
<Animated.View
style={[styles.animatedChild, { transform: [{ scale }] }]}
onStartShouldSetResponder={() => true}
onResponderMove={handleTouchMove}
>
{children}
</Animated.View>
</TouchableWithoutFeedback>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
animatedChild: {
justifyContent: "center",
alignItems: "center",
},
});
export default ShrinkOnTouch;