Home > Net >  React native gesture handler
React native gesture handler

Time:09-17

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();
    }
  };

  • Related