In this lesson we'll use PanResponder
gesture handlers to add the ability to swipe a modal closed in either direction. We'll need ScrollView
callbacks to allow us to still scroll through comments and only close when desired.
We'll use the Animated.setValue
function to translate
our Modal up or down depending on the direction of the swipe.
[00:00] Let's take a look at what we currently have set up. We have an animated view that's our modal. Inside of that we have a comment wrapping a ScrollView. Inside of our ScrollView we have some fake text at the top, we have a fake comment in the middle which is just a view with 1000 pixels of height and some background color. Then we have some fake text at the bottom so we know when we hit the bottom. In our componentWillMount, we'll create a few things.
[00:27] The first will be this.animated, which is just going to be a new Animated.Value that we can control, then additionally, we'll create this.animatedMargin, which is a new Animated.Value as well, which will help us add the crushing swipe-down effect to our modal. Then we'll also create this.scrollOffset, this.contentHeight, and this.ScrollViewHeight, and we'll just initialize those all to 0We'll go ahead and we'll set up our stylings for our animated values. So we'll say const spacerStyle = marginTop. Then we'll just pass in our this.animatedMargin, we'll then do a const opacityInterpolate, and we'll operate off this.animated.interpolate(). This will take an input range, because we can fade going both up and down, we'll start our input range at -400, 0and then 400, so that we can use the same opacityInterpolate for both interactions.
[01:37] Then our output range will be 01, and 0Now we can set up our modal style, so we'll say const modalStyle is equal to an object transform, which takes a translateY. We'll just pass in this.animated so that we can control the swiping up and down, or moving our modal out of the screen, then we'll also apply our opacity here with our opacityInterpolate.
[02:07] Now let's set up our styles an apply them to our certain views. We'll create an animated view here, we'll say style equals our margin or our spacerStyle, we'll then also add in our modalStyle to our modal. We'll also need to capture some of the data from our ScrollView. The first thing we'll do is add ScrollEventThrottle and we'll set it to 60ms, so that we're notified every 60 milliseconds that a scroll happened.
[02:39] In our onScroll event callback we'll say event, and then we'll assign the properties that we set up above. So this.scrollOffset will be equal to our event.nativeEvent.contentOffset.Y, and then we'll say this.ScrollViewHeight = event.nativeEvent.layoutMeasurements.Height.
[03:12] We'll also get the content size of the entire ScrollView innards, so let's say onContentSizeChange, which will then receive a contentWidth, and a contentHeight. However, we currently only care about the height, so we'll say this.contentHeight = contentHeight.
[03:35] Now let's go back up to our ComponentWillMount, and we'll simply create a panResponder. We'll assign it to this.panResponder. The panResponder will allow us to act on the gestures, and when we want to start and stop them. First, we'll remove the onStartShouldSetPanResponder, and we'll focus on the move only. This will allow us to control when we want to start our takeover of the gestures.
[04:07] We'll say const DY which is our delta Y, which is our gesture state, and we've got our totalScrollHeight, which is totalScrollHeight which is a combination of this.ScrollOffset plus this.ScrollViewHeight. Now we want to set up our conditions for when this panResponder should respond. We'll say if, and our first will be this.scrollOffset >= 0and DY > 0this one is for when we're at the top and we're dragging downwards, and we want to crush our modal out of the way.
[04:48] The second one will be for when we're at the bottom and we're dragging up, and we want to swipe it away. This we can tell by saying totalScrollHeight >= this.contentHeght and our DY < 0In these cases, we want to return true, which will trigger our panResponder and allow us to interact with the gesture.
[05:14] Now let's set up when we actually do our move. We can delete the onPanResponderGrant, and just deal with our move. Say const DY is equal to our gesture state, and if our DY < 0then we'll say this.animated.setValue(DY), our delta Y, otherwise if our DY > 0we just control our animatedMargins with this.animatedMargin.setValue(DY). To make this example work, we need to go down to our modal, and do our spread of this.panResponder.panHandlers and we'll spread those onto our modal.
[06:05] Now we can go ahead and refresh our emulator. We can see that if we scroll down, and once we get to the bottom, we can swipe our modal away, or if we scroll down and then come back to the top and drag down, we can actually crush our modal. Finally, we need to control what happens when we release. We'll get our DY from our gestureState, and if our delta Y is less than 150, which is just an arbitrary threshold, then we'll do an animated parallel. We'll say animated.timing on this.animated.
[06:43] We'll say toValue:-400 which translateY will make our modal go up, and then a duration of 150, then we'll also do an animated.timing on this.animatedMargin, and we'll just animate that back to 0of a duration of 150. Now we'll set up our else if, if our DY > -150 and our DY < 150, we haven't gone anywhere outside of our threshold so we'll do an animated.parallel, and we'll do animated.timing(this.animated { toValue:0duration:150}) and then we'll also animated.timing(this.animatedMargin {toValue:0duration:150}).
[07:37] This will allow us to release, and then go back to a neutral position. Finally else if our DY > 150, we want to do an animated.timing on this.animated, and in this case we're swiping down, so we'll just say toValue of 400, and a duration of 300ms. Now we'll also need to call start on all of these, otherwise our animations won't trigger.
[08:09] We'll go call start on our parallel, and also call start on our parallel up here. If we refresh our emulator you can see that we can crush it and it will snap back, or if we hit our threshold then it will zoom away and it will fade out, or we can scroll to the bottom. Once we're at the bottom we can swipe it away and it will snap back if we don't hit 150, or we can hit the threshold, and it will zoom away.