1

I'm new to react-native animation API and PanResponder API.
I want to create google maps clone UI and animations of card which is show after results.
I want to create card and when user swipe up, the card you reach to top of screen and when user swipe down, the card should resize to original position.
Demo of Google Maps that i want to achieve


I have implemented some of code for that, but i am facing problem like card is moving out of screen in top direction (swiping up after card is reach to top position). Sometimes, swipe down works but not always.


My demo

Expo Snack Link


App.js

import React, { useEffect, useState } from "react";
import {
  SafeAreaView,
  View,
  Text,
  Dimensions,
  PanResponder,
  Animated,
} from "react-native";
import { Searchbar, FAB } from "react-native-paper";
import MapView from "react-native-maps";
import * as Location from "expo-location";


const SCREEN_HEIGHT = Dimensions.get("window").height;
const SCREEN_WIDTH = Dimensions.get("window").width;

const App = () => {
  

  const [latitute, setLatitute] = useState(0);
  const [longitute, setLongitute] = useState(0);
  const [isLoading, setIsLoading] = useState(true);


  const pan = useState(
    new Animated.ValueXY({ x: 0, y: SCREEN_HEIGHT - 200 })
  )[0];


  const panResponder = useState(
    PanResponder.create({
      onMoveShouldSetPanResponder: () => true,
      onPanResponderGrant: () => {
        pan.extractOffset();
        return true;
      },
      onPanResponderMove: (e, gestureState) => {
        pan.setValue({ x: 0, y: gestureState.dy });
      },
      onPanResponderRelease: (e, gestureState) => {
        
        if (gestureState.moveY > SCREEN_HEIGHT - 200) {
          Animated.spring(pan.y, {
            toValue: 0,
            tension: 1,
            useNativeDriver: true,
          }).start();
        } else if (gestureState.moveY < 200) {
          Animated.spring(pan.y, {
            toValue: 0,
            tension: 1,
            useNativeDriver: true,
          }).start();
        } else if (gestureState.dy < 0) {
          Animated.spring(pan.y, {
            toValue: -SCREEN_HEIGHT + 200,
            tension: 1,
            useNativeDriver: true,
          }).start();
        } else if (gestureState.dy > 0) {
          Animated.spring(pan.y, {
            toValue: SCREEN_HEIGHT - 200,
            tension: 1,
            useNativeDriver: true,
          }).start();
        }
      },
    })
  )[0];

  const animatedHeight = {
    transform: pan.getTranslateTransform(),
  };

  useEffect(() => {
    (async () => {
      let { status } = await Location.requestPermissionsAsync();

      let location = await Location.getCurrentPositionAsync({});
      console.log(location);
      setLatitute(location.coords.latitude);
      setLongitute(location.coords.longitude);
      setIsLoading(false);
    })();
  }, []);

  return (
    <SafeAreaView
      style={{
        display: "flex",
        flex: 1,
      }}
    >
      {!isLoading && (
        <View>
          <MapView
            style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT }}
            showsUserLocation
            initialRegion={{
              latitude: latitute,
              longitude: longitute,
              latitudeDelta: 0.01,
              longitudeDelta: 0.01,
            }}
          />

          <Searchbar
            placeholder="Search"
            style={{
              position: "absolute",
              top: 10,
              margin: 10,
            }}
            
            icon="menu"
            onIconPress={() => {}}
          />

          <FAB
            style={{
              position: "absolute",
              top: SCREEN_HEIGHT * 0.8,
              left: SCREEN_WIDTH * 0.8,
            }}
            icon="plus"
            onPress={() => console.log("Pressed")}
          />

          <Animated.View
            style={[
              animatedHeight,
              {
                position: "absolute",
                right: 0,
                left: 0,
                width: SCREEN_WIDTH,
                height: SCREEN_HEIGHT,
                backgroundColor: "#0af",
                borderTopLeftRadius: 25,
                borderTopRightRadius: 25,
                zIndex: 10,
              },
            ]}
            {...panResponder.panHandlers}
          >
            <View>
              <Text>Hi</Text>
            </View>
          </Animated.View>
        </View>
      )}
    </SafeAreaView>
  );
};

export default App;


Ferin Patel
  • 3,424
  • 2
  • 17
  • 49

1 Answers1

2

if you wanna make bottomsheet then you can use react-native-bottomsheet-reanimated

yarn add react-native-bottomsheet-reanimated
import React, { useEffect, useState } from "react";
import {
  SafeAreaView,
  View,
  Text,
  Dimensions,
  PanResponder,
  Animated,
  StyleSheet
} from "react-native";
import { Searchbar, FAB } from "react-native-paper";
import MapView from "react-native-maps";
import * as Location from "expo-location";
import BottomSheet from "react-native-bottomsheet-reanimated";



const SCREEN_HEIGHT = Dimensions.get("window").height;
const SCREEN_WIDTH = Dimensions.get("window").width;

const App = () => {
  

  const [latitute, setLatitute] = useState(0);
  const [longitute, setLongitute] = useState(0);
  const [isLoading, setIsLoading] = useState(true);


  const pan = useState(
    new Animated.ValueXY({ x: 0, y: SCREEN_HEIGHT - 200 })
  )[0];

  useEffect(() => {
    (async () => {
      let { status } = await Location.requestPermissionsAsync();

      let location = await Location.getCurrentPositionAsync({});
      console.log(location);
      setLatitute(location.coords.latitude);
      setLongitute(location.coords.longitude);
      setIsLoading(false);
    })();
  }, []);

  return (
    <SafeAreaView
      style={{
        display: "flex",
        flex: 1,
      }}
    >
      {!isLoading && (
        <View>
          <MapView
            style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT }}
            showsUserLocation
            initialRegion={{
              latitude: latitute,
              longitude: longitute,
              latitudeDelta: 0.01,
              longitudeDelta: 0.01,
            }}
          />

          <Searchbar
            placeholder="Search"
            style={{
              position: "absolute",
              top: 10,
              margin: 10,
            }}
            
            icon="menu"
            onIconPress={() => {}}
          />

          <FAB
            style={{
              position: "absolute",
              top: SCREEN_HEIGHT * 0.8,
              left: SCREEN_WIDTH * 0.8,
            }}
            icon="plus"
            onPress={() => console.log("Pressed")}
          />

          <BottomSheet
          bottomSheerColor="#FFFFFF"
          initialPosition={"30%"}  //200, 300
          snapPoints={["30%","100%"]}
          isBackDropDismisByPress={true}
          isRoundBorderWithTipHeader={true}
          containerStyle={{backgroundColor:"#0af"}}
          header={
            <View>
              <Text style={styles.text}>Header</Text>
            </View>
          }
          body={
            <View style={styles.body}>
              <Text>Hi</Text>
            </View>
          }
        />

        </View>
      )}
    </SafeAreaView>
  );
};


export default App;

const styles = StyleSheet.create({
  body:{
    justifyContent:"center",
    alignItems:"center"
  },
  text:{
    fontSize:20,
    fontWeight:"bold"
  }
});

enter image description here

Muhammad Numan
  • 23,222
  • 6
  • 63
  • 80
  • I was looking for the answers using PanResponder API. If no better answer came, i would accept your answer. – Ferin Patel Jul 15 '20 at 11:16
  • is it any way to make this bottom sheet to make scrollable, instead of having 100% height ?? Please reply if you know anything to make it scrollable ? – Ferin Patel Jul 18 '20 at 05:18
  • @FerinPatel currently this package does not support scrollable bottom sheet but I will add this support soon and let you know – Muhammad Numan Jul 18 '20 at 07:23
  • @MuhammadNuman I came across lots of React native maps. Your answer is one of best. I have issues with geofencing. I asked in stack-overflow community. Can you please help me out of this: https://stackoverflow.com/questions/64816066/react-native-maps-zoom-in-and-zoom-out-filter-data – Krisna Nov 13 '20 at 06:56