I am really new to React native Animation. I am trying create Custom Bottom sheet. I have made to two Animation function. Those functions will trigger transform translateY postion change. I have created two separates buttons and calling those functions. The target animated view moves up and down based when the button press. I want to scroll them drag-able. If the Animated view drag to top the View will go up and if the the Animated view drag to down View will go up. For this Gesture Movement I used PanGestureHandler
from react-native-gesture-handler
. I used bad-logic inside the gesture-handler function and use those function, it goes down which it drag to bottom but it does not goes up when I tried to drag to up. I really don't know how to fix this issue.
I share my code in expo-snack
This is all my code
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Dimensions,
useWindowDimensions,
SafeAreaView,
Animated,
Button,
} from "react-native";
import MapView from "react-native-maps";
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
TouchableOpacity,
} from "react-native-gesture-handler";
type AnimatedGHContext = {
startX: number;
startY: number;
};
const { width } = Dimensions.get("screen");
const initialRegion = {
latitudeDelta: 15,
longitudeDelta: 15,
latitude: 60.1098678,
longitude: 24.7385084,
};
export default function App() {
const { height } = useWindowDimensions();
const [translateY] = useState(new Animated.Value(0));
const bringUpActionSheet = () => {
Animated.timing(translateY, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
};
const closeDownBottomSheet = () => {
Animated.timing(translateY, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
};
const bottomSheetIntropolate = translateY.interpolate({
inputRange: [0, 1],
outputRange: [-height / 2.4 50, 0],
});
const animatedStyle = {
transform: [
{
translateY: bottomSheetIntropolate,
},
],
};
const gestureHandler = (e) => {
console.log(e.nativeEvent.state); // THIS IS NOT GOOD CONDITION
if (e.nativeEvent.state) {
closeDownBottomSheet();
} else if (e.nativeEvent.state < 4) {
bringUpActionSheet();
}
};
return (
<View style={{ flex: 1 }}>
<MapView style={styles.mapStyle} initialRegion={initialRegion} />
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View
style={[styles.container, { top: height * 0.7 }, animatedStyle]}
>
<Animated.View style={styles.grbber} />
<SafeAreaView style={styles.wrapper}>
<View style={styles.content}>
<Text style={styles.title}>I am scroll sheet</Text>
<TouchableOpacity
style={styles.button}
onPress={() => closeDownBottomSheet()}
>
<Text style={styles.title}>Close</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => bringUpActionSheet()}
>
<Text style={styles.title}>Up</Text>
</TouchableOpacity>
<View style={styles.fakeContent} />
</View>
</SafeAreaView>
</Animated.View>
</PanGestureHandler>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: "absolute",
top: 0,
left: 0,
right: 0,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
height: -6,
width: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,
borderTopEndRadius: 15,
borderTopLeftRadius: 15,
},
mapStyle: {
width: width,
height: 800,
},
content: {
flex: 1,
padding: 20,
},
title: {
fontWeight: "400",
fontSize: 22,
},
fakeContent: {
flex: 1,
height: 1000,
},
grbber: {
width: 60,
alignSelf: "center",
marginTop: 5,
borderTopWidth: 5,
borderTopColor: "#aaa",
},
button: {
alignItems: "center",
justifyContent: "center",
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: "green",
marginBottom: 10,
height: 50,
},
});
CodePudding user response:
I think you should use The animated view alongside panResponder from the react-native library to handle movements and to track the value of the x & y positions of the view and make the changes to the translateY state in onPanResponderRelease function, Check this:
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
Dimensions,
useWindowDimensions,
SafeAreaView,
Animated,
PanResponder,
Button,
} from 'react-native';
import MapView from 'react-native-maps';
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
TouchableOpacity,
} from 'react-native-gesture-handler';
type AnimatedGHContext = {
startX: number,
startY: number,
};
const { width } = Dimensions.get('screen');
const initialRegion = {
latitudeDelta: 15,
longitudeDelta: 15,
latitude: 60.1098678,
longitude: 24.7385084,
};
export default function App() {
const { height } = useWindowDimensions();
const [translateY] = useState(new Animated.Value(0));
const bringUpActionSheet = () => {
Animated.timing(translateY, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
};
const closeDownBottomSheet = () => {
Animated.timing(translateY, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
};
const bottomSheetIntropolate = translateY.interpolate({
inputRange: [0, 1],
outputRange: [-height / 2.4 50, 0],
});
const animatedStyle = {
transform: [
{
translateY: bottomSheetIntropolate,
},
],
};
[];
const pan = React.useRef(new Animated.ValueXY()).current;
const panResponder = React.useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
//Add any functions you want here when the user lets go of the view
},
})
).current;
return (
<View style={{ flex: 1 }}>
<MapView style={styles.mapStyle} initialRegion={initialRegion} />
<Animated.View
style={[styles.container, { top: height * 0.7 }, animatedStyle]}
{...panResponder.panHandlers}>
<Animated.View
style={[
{
transform: [{ translateX: pan.x }, { translateY: pan.y }],
},
styles.grbber,
]}
{...panResponder.panHandlers}
/>
<SafeAreaView style={styles.wrapper}>
<View style={styles.content}>
<Text style={styles.title}>I am scroll sheet</Text>
<TouchableOpacity
style={styles.button}
onPress={() => closeDownBottomSheet()}>
<Text style={styles.title}>Close</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => bringUpActionSheet()}>
<Text style={styles.title}>Up</Text>
</TouchableOpacity>
<View style={styles.fakeContent} />
</View>
</SafeAreaView>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: 'white',
shadowColor: 'black',
shadowOffset: {
height: -6,
width: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,
borderTopEndRadius: 15,
borderTopLeftRadius: 15,
},
mapStyle: {
width: width,
height: 800,
},
content: {
flex: 1,
padding: 20,
},
title: {
fontWeight: '400',
fontSize: 22,
},
fakeContent: {
flex: 1,
height: 1000,
},
grbber: {
flex: 1,
width: 60,
alignSelf: 'center',
marginTop: 5,
borderTopWidth: 5,
borderTopColor: '#aaa',
},
button: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: 'green',
marginBottom: 10,
height: 50,
},
});
CodePudding user response:
I made this logic. and it works perfect :)
const gestureHandler = (e: PanGestureHandlerGestureEvent) => {
if (e.nativeEvent.translationY > 0) {
closeDownBottomSheet();
} else if (e.nativeEvent.translationY < 0) {
bringUpActionSheet();
}
};