15

I'm making a React-Native app and the scenario is this: I want the user to be able to pan a view, but not wholly the way he wants. I'd like to constraint how much the view can move when being dragged by the user.

I've read through the documentation of both the PanResponder and Animated API (multiple times), but can't find anything that does this, neither can I find anyone else that implemented something alike.

Constraining in the panresponder's event?

onPanResponderMove: Animated.event([null,{
    dx: this.state.pan.x,  //Some function here to constrain the value?
    dy: this.state.pan.y
}]),

Constraining while applying the transforms?

style = {
    transform: {
        transformX: ...,//Use interpolate to constrain the values somehow?
        transformY: ...
    }
}
stinodes
  • 1,269
  • 10
  • 15
  • Check the [Animated.Value](https://facebook.github.io/react-native/docs/animated.html), it allow you to control any "Animate", takes 0 - 1 values, but you could use it to check especific `MAX` and `MIN` values for your view – DIEGO CARRASCAL Apr 20 '16 at 14:27
  • 1
    There is interpolate, but I don't think that actually constraints the values, does it? As far as I can tell, it only remaps them. – stinodes Apr 20 '16 at 14:31
  • 2
    interpolate does support constrain by using extrapolation. From the docs: You can set the extrapolation by setting the extrapolate, extrapolateLeft or extrapolateRight options. The default value is extend but you can use clamp to prevent the output value from exceeding outputRange. – David Apr 21 '16 at 08:41

5 Answers5

12

Use the extrapolate, extrapolateRight or extrapolateLeft properties:

const maxX = 200;
const constrainedX = this.state.pan.x.interpolate({
  inputRange: [0, maxX],
  outputRange: [0, maxX],
  extrapolateRight: 'clamp'
})

https://facebook.github.io/react-native/docs/animations.html#interpolation

deb0ch
  • 1,133
  • 2
  • 16
  • 25
  • Working like a charm. I was stuck on `Animated.diffClamp` for a while until found this answer – Rotem Aug 18 '20 at 15:51
7

The solution I came up with: Since Animated.event() returns a function, you can just pass your processed gestureState, with constrained values, into that function.
It'd look something like this:

onPanResponderMove: (evt, gestureState) => {
    let newdx = gestureState.dx, //DO SOMETHING WITH YOUR VALUES
        newdy = gestureState.dy;
    Animated.event([null,{
        dx: this.state.pan.x,
        dy: this.state.pan.y
    }])(evt, {dx: newdx, dy: newdy});
},

Whether it's it should be done is debatable though.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
stinodes
  • 1,269
  • 10
  • 15
6

The solution provided by @stinodes will degrade performance. Why not using interpolate in the following way:

const maxX = 200;
const constrainedX = this.state.pan.x.interpolate({
  inputRange: [0, maxX, Infinity],
  outputRange: [0, maxX, maxX]
})
mieszko4
  • 301
  • 2
  • 6
2
const constrainedX = this.state.pan.x.interpolate({
      inputRange: [leftLimit, 0, rightLimit],
      outputRange: [leftLimit, 0, rightLimit],
      extrapolate: 'clamp'
})

extapolate: 'clamp' will keep it in bounds.

MichałKraków
  • 111
  • 1
  • 7
0

Although this thread is old, but I still didn't find other people mentioning the caveat that with interpolate and clamp method, the component might seem stuck there. I have posted my solution capping the pan offset, at ReactNative PanResponder limit X position also Lock movement with PanResponder in react native so I won't repeat here.

Edit: here is the essential code and a little more explanation

      onPanResponderGrant: () => {
        console.log("pan responder was granted access!")
        pan.setOffset({
          x: (pan.x._value>xMax)? xMax : (pan.x._value<xMin)? xMin: pan.x._value,
          y: (pan.y._value>yMax)? yMax : (pan.y._value<yMin)? yMin: pan.y._value,
        });
      },

Without this, even with clamp, one may have problems to move the view near the limit b/c he doesn't know what is the input, dx, the accumulated distance from the origin. He only knows it will be capped. If he accidentally moved far outside the limit, and then wants to move the view back, he will have no idea how much swipe he needs. With the pan.setOffset, one can limit the input, dx, so there will be no guessing when one moves the view near the limit.

letmeask
  • 43
  • 7