Create swipable modal like facebook’s comment modal

Spread the love

Hi guys, today I will create a swipable modal like facebook’s comment modal.

I would like to share it here so you guys can have an idea how to create a similar thing using react-native.

As you see in the video, the modal will be appeared from the bottom then swipe up slowly then stop at the top edge. User can also use there finger to swipe it up or down to make it disappeared.

Using this way, you just need to custom a little bit in the code the make it works as you want it does.

Let’s start.

The idea is: we will use react-native component “Modal” to make it show in fullscreen mode and display on top of the screen. We won’t use any animation properties of this “Modal” component, we will create our own animation using Animated which is provided by react-native.

Let’s have a look at the react-native components which we will use in this modal.

There are 3 main components which we will use to create this.

  1. Modal: use as the main component
  2. Animated: handle our custom animation (move from bottom to top)
  3. PanResponder: handle event when users use there fingers to move the modal up or down

Firstly, we create a class name CommentModal, let’s initialize the basic class structure

As you can see, the animationType prop in Modal we are not using it, instead we set it to “none”.

We also declare two proptypes: onClose and visible to be able to control the visibility and event onClose of the modal.

Now, we will create our own animation.

In the constructor, we declare two variables: pan and isAnimating.

  1. Pan: initial animation value with x and y. (refer to this https://facebook.github.io/react-native/docs/animated#docsNav for more information about it). In this modal, we only handle the swipable up and down, so x value is always 0, the initial y value will be screenHeight, because it will move from bottom to top.
  2. IsAnimating: to handle the panResponder, to avoid the conflict of the animation when the modal is moving.

Next step: We will create the animation move the modal from bottom to top when the modal is visible.

We create ComponentWillReceiveProps to catch the property “visible” and show modal with animation moving up from bottom to top. The modal need to be always existed in the place where we want to use it but the visible will be controlled and pass to it.

You can see in the showCommentAnimation function, we are using Animated.timing to move the model to position (0,0), mean the top edge of the screen.

The TIMING_CONFIG, we declared at the beginning with duration 300 and type of easing ( more detail here (https://facebook.github.io/react-native/docs/easing)

OK, we are done for the appear of the modal.

Now we will continue handle the panResponder to interact with the the modal by fingers.

Very easy, in this step, we will declare function componentWillMount.

componentWillMount() {
    if (!this) {
      return;
    }
    this.animatedValueX = 0;
    this.animatedValueY = 0;
    this.state.pan.x.addListener(value => (this.animatedValueX = value.value));
    this.state.pan.y.addListener(value => (this.animatedValueY = value.value));

    this.panResponder = PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: () => false,
      onStartShouldSetPanResponderCapture: () => false,
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        if (this.state.isAnimating) {
          return false;
        }
        if (gestureState.dy > 30 || gestureState.dy < -30) {
          return true;
        }
        return false;
      },
      onPanResponderGrant: () => {
        this.state.pan.setOffset({
          x: this.animatedValueX,
          y: this.animatedValueY,
        });
        this.state.pan.setValue({ x: 0, y: 0 }); // Initial value
      },
      onPanResponderMove: (evt, gestureState) => {
        this.setState({
          backgroundColor: 'transparent',
        });
        this.state.pan.setValue({ x: 0, y: gestureState.dy });
      },
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
        // this.state.pan.flattenOffset(); // Flatten the offset so it resets the default positioning
        if (gestureState.vy <= -0.7 || gestureState.dy <= -300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: -screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else if (gestureState.vy >= 0.5 || gestureState.dy >= 300) {
          Animated.timing(this.state.pan, {
            toValue: { x: 0, y: screenHeight },
            ...TIMING_CONFIG,
          }).start(() => {
            this.props.onClose();
          });
        } else {
          Animated.spring(this.state.pan, {
            toValue: 0,
          }).start();
          this.setState({
            backgroundColor: '#000',
          });
        }
      },
    });
  }

Let’s look at the code above:

  1. We declare panResponder to catch and handle event when user use there fingers to touch and move the modal.

onStartShoudSetPanResponder and onStartShoudSetPanResponderCapture, we set them to false, because we don’t need to catch those events, at the moment the modal appeared, we handle animation ourself (function showCommentModalAnimation).

We catch event onMoveShouldSetPanResponder, the condition inside the callback will return true if dy (the distance of moving y direction) > 30 or < -30. The reason is that we want to make sure what the finger is moving after touching to the screen. At the moment finger touch the screen, the dy will be 0, if it moves, the dy will change up or down.

We also check isAnimating value because we don’t want it to be conflict to the animation we create at the beginning (showCommentModalAnimation)

onPanResponderGrant

 

Inside this callback, we use setOffset to set the position at the moment the finger touch to the screen, to prevent the effect of the modal will be suddenly moved.

this.animatedValueX, this.animatedValueY we declared above.

And also we set the modal position to (0,0)

onPanResponderMode, we set the value dy to state pan to make the modal moving along with the finger.

onPanResponderRelease: We catch the velocity and the distance of the moving by Y direction to make the modal disappear. We also use the Animated Timing to make it move up or down to position -screenHeight or screenHeight.

Now we replace the modal body by Animated View to see the effect of the animation we have just created above

The first Animated.View to handle the background opacity will change from 0 -> 1 base of the position of the modal. We here use interpolate to get the value base on the modal position. More detail about interpolate here (https://facebook.github.io/react-native/docs/0.8/animations#interpolation

const interpolateBackgroundOpacity = this.state.pan.y.interpolate({
      inputRange: [-screenHeight, 0, screenHeight],
      outputRange: [0, 1, 0],
    });

handleGetStyle will handle the moving of the modal

We use transform to move the modal by position (x,y). More detail https://facebook.github.io/react-native/docs/animatedvaluexy#gettranslatetransform

Opacity just for the effect of appearance.

Now, It’s ready to run.

How to use this modal

Here is the fullcode, you guys can have look at it (here)

Please leave a comment bellow if you have any inquiries.

Leave a Reply

Your email address will not be published. Required fields are marked *