Jordan
Jordan

Reputation: 2523

How to translate view offset based on the current drag position of parent

I have a super specific case here that is kind of tripping me up on how the math needs to work out to get this to work

I have this card rendered on the page

enter image description here

Which is rendered with these styles...

-------- main card --------
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height,
shadowColor: "#455B63",
shadowOffset: {
  width: 0,
  height: 0,
},
shadowOpacity: 0.33,
shadowRadius: 16,
borderBottomLeftRadius: 12,
borderBottomRightRadius: 12

-------- content view --------
backgroundColor: '#fff',
paddingVertical: 14,
paddingHorizontal: 20,
height: height * 2

With this animation attached to it...

Animated.spring(this.state.placeCard,{
    toValue: {x: 0, y: deviceHeight - placeCardClosedHeight},
    duration: 350,
}).start();

The content view has this animation state set in the state

new Animated.ValueXY({x: 0, y: -(Math.round(deviceHeight / 2) - 160)})

And I'm trying to get it to animate to this layout

enter image description here

The panhandler I have set up for handle the overall card styles is this

this._placeCardPanResponder = PanResponder.create({
  onMoveShouldSetPanResponderCapture: (evt, gestureState) => gestureState.dx != 0 && gestureState.dy != 0,
  onPanResponderGrant: (evt, gestureState) => {
    this.setState({
      placeCardTopOffset: this.state.placeCard.__getValue().y
    })
  },
  onPanResponderMove: (evt, gestureState) => {
    let y = gestureState.dy;
    const { placeCardTopOffset } = this.state;
    if (y + placeCardTopOffset <= 0) {
      y = 0
    } else if (y + placeCardTopOffset >= deviceHeight - placeCardClosedHeight) {
      y = deviceHeight - placeCardClosedHeight
    } else {
      y = y + placeCardTopOffset;
    }
    this.state.placeCard.setValue({x: 0, y});
  },
  onPanResponderRelease: (evt, gestureState) => {
    if (gestureState.dy < 0) {
      if (this.state.placeCard.__getValue().y > 0) {
        Animated.timing(
          this.state.placeCard,
          {
            toValue: {x: 0, y: 0},
            duration: 175,
          }
        ).start();
      }
    }
    if (gestureState.dy > 0) {
      if (this.state.placeCard.__getValue().y < deviceHeight - placeCardClosedHeight) {
        Animated.timing(
          this.state.placeCard,
          {
            toValue: {x: 0, y: deviceHeight - placeCardClosedHeight},
            duration: 175,
          }
        ).start();
      }
    }
  }
});

When the card is full height on the page the content view would be animated something like this

Animated.timing(
  this.state.placeCardContent,
  {
    toValue: {x: 0, y: 0},
    duration: 175,
  }
).start();

I have a snack setup to show this off

If you click on the image you will see the intended animation but if you drag you will see how the dragging is being handled. Where I am having issues is with trying to figure out the math related to animating that content view while the main view is being dragged.

https://snack.expo.io/B1lhq5Rb4

Upvotes: 0

Views: 682

Answers (1)

Jordan
Jordan

Reputation: 2523

I was neglecting to remember the fact that react-native's Animated API has a .interpolate() function which was designed for this vary task.

Here is the code to make this work...

<Animated.View style={{...style.placeCardContent, transform: [{
    translateX: 0
},{
    translateY: this.state.placeCard.y.interpolate({
        inputRange: [0, deviceHeight - placeCardClosedHeight],
        outputRange: [0, -(Math.round(deviceHeight / 2) - 160)]
    })
}]}}>

You can see this working in this snack example

https://snack.expo.io/@jordanr/carddrag

Upvotes: 2

Related Questions