I'm currently learning React Native and I have been stuck on my component for several hours. It is an image swipe system. I know there are a lot of packages that do what I want, but I'd rather create my own system to learn better.
Here is the description of my problem: The first swipe works perfectly well but each time I swipe the user interface freezes for a second and I can't figure out why.
Here you can try my code : https://snack.expo.dev/-1yjXbe4M
import { useEffect, useState } from "react";
import {
Text,
View,
Dimensions,
Image,
Animated,
PanResponder,
} from "react-native";
const SCREEN_HEIGHT = Dimensions.get("window").height;
const SCREEN_WIDTH = Dimensions.get("window").width;
const GetMovies = () => {
return [
{
poster_path: "/gOnmaxHo0412UVr1QM5Nekv1xPi.jpg",
},
{
poster_path: "/vZloFAK7NmvMGKE7VkF5UHaz0I.jpg",
},
{
poster_path: "/kuf6dutpsT0vSVehic3EZIqkOBt.jpg",
},
{
poster_path: "/x3PIk93PTbxT88ohfeb26L1VpZw.jpg",
},
{
poster_path: "/zCdzPK6fJgQL0FeKeYkciZzjyOL.jpg",
},
{
poster_path: "/sv1xJUazXeYqALzczSZ3O6nkH75.jpg",
},
{
poster_path: "/dm06L9pxDOL9jNSK4Cb6y139rrG.jpg",
},
{
poster_path: "/t6HIqrRAclMCA60NsSmeqe9RmNV.jpg",
},
{
poster_path: "/qi9r5xBgcc9KTxlOLjssEbDgO0J.jpg",
},
{
poster_path: "/aCy61aU7BMG7SfhkaAaasS0KzUO.jpg",
},
{
poster_path: "/3GrRgt6CiLIUXUtoktcv1g2iwT5.jpg",
},
{
poster_path: "/kUWTY8rwEZ3d8G31GuPMbvqS67D.jpg",
},
{
poster_path: "/v5CfpzxoJDkZxjZAizClFdlEF0U.jpg",
},
{
poster_path: "/hrATQE8ScQceohwInaMluluNEaf.jpg",
},
];
};
const Swiper = () => {
const [movies, setMovies] = useState([]);
const [currentIndex, setCurrentIndex] = useState(0);
const position = new Animated.ValueXY();
useEffect(() => {
setMovies(GetMovies());
}, []);
useEffect(() => {
position.setValue({ x: 0, y: 0 });
}, [currentIndex]);
const rotate = position.x.interpolate({
inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
outputRange: ["-10deg", "0deg", "10deg"],
extrapolate: "clamp",
});
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderMove: (evt, gestureState) => {
position.setValue({ x: gestureState.dx, y: gestureState.dy });
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > 120) {
Animated.spring(position, {
toValue: { x: SCREEN_WIDTH + 100, y: gestureState.dy },
useNativeDriver: true,
}).start(() => {
setCurrentIndex(currentIndex + 1);
});
} else if (gestureState.dx < -120) {
Animated.spring(position, {
toValue: { x: -SCREEN_WIDTH - 100, y: gestureState.dy },
useNativeDriver: true,
}).start(() => {
setCurrentIndex(currentIndex + 1);
});
} else {
Animated.spring(position, {
toValue: { x: 0, y: 0 },
friction: 4,
useNativeDriver: true,
}).start();
}
console.log("PanResponder release");
},
});
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 nopeOpacity = 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",
});
return (
<View style={{ flex: 1 }}>
{console.log("rendering")}
<View style={{ height: 60 }} />
<View style={{ flex: 1 }}>
{movies &&
movies
.map((item, i) => {
console.log(item);
if (i < currentIndex) {
return null;
} else if (i == currentIndex) {
return (
<Animated.View
{...panResponder.panHandlers}
key={i}
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,
}}
>
GAUCHE
</Text>
</Animated.View>
<Animated.View
style={{
opacity: nopeOpacity,
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,
}}
>
DROITE
</Text>
</Animated.View>
<Image
style={{
flex: 1,
height: null,
width: null,
resizeMode: "cover",
borderRadius: 20,
}}
source={{
uri: `https://image.tmdb.org/t/p/w200${item.poster_path}`,
}}
/>
</Animated.View>
);
} else {
return (
<Animated.View
key={i}
style={{
opacity: nextCardOpacity,
transform: [{ scale: nextCardScale }],
height: SCREEN_HEIGHT - 120,
width: SCREEN_WIDTH,
padding: 10,
position: "absolute",
}}
>
<Image
style={{
flex: 1,
height: null,
width: null,
resizeMode: "cover",
borderRadius: 20,
}}
source={{
uri: `https://image.tmdb.org/t/p/w200${item.poster_path}`,
}}
/>
</Animated.View>
);
}
})
.reverse()}
</View>
</View>
);
};
export default Swiper;