Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactNative PanResponder limit X position

I'm building a Music Player and I'm focusing on the progress bar. I was able to react to swipe gestures, but I cant limit how far that gesture goes.

This is what I've done so far. I've reduced everything to the minumal:

constructor(props) {
    super(props);

    this.state = {
      pan: new Animated.ValueXY()
    };
}

componentWillMount() {
    this._panResponder = PanResponder.create({
        onMoveShouldSetResponderCapture: () => true,
        onMoveShouldSetPanResponderCapture: () => true,
        onPanResponderGrant: (e, gestureState) => {


            // Set the initial value to the current state
            let x = (this.state.pan.x._value < 0) ? 0 : this.state.pan.x._value;


            this.state.pan.setOffset({ x, y: 0 });
            this.state.pan.setValue({ x: 0, y: 0 });


        },
        onPanResponderMove: Animated.event([
            null, { dx: this.state.pan.x, dy: 0 },
        ]),
        onPanResponderRelease: (e, { vx, vy }) => {
            this.state.pan.flattenOffset();
        }
    });
}

render() {
    let { pan } = this.state;

    // Calculate the x and y transform from the pan value
    let [translateX, translateY] = [pan.x, pan.y];
    // Calculate the transform property and set it as a value for our style which we add below to the Animated.View component
    let imageStyle = { transform: [{ translateX }, { translateY }] };

    return (
        <View style={styles.container}>
            <Animated.View style={{imageStyle}} {...this._panResponder.panHandlers} />
        </View>
    );
}

Here there is an image showing what the problem is.

Initial position:

Initial position

Wrong Position, limit exceeded:

Wrong position

So the idea is to stop keeping moving once the limit (left as well as right) is reached. I tried checking if _value < 0, but it didn't work since It seems to be an offset, not a position.

Well any help will be appreciated.

like image 784
Facundo La Rocca Avatar asked Sep 13 '17 03:09

Facundo La Rocca


People also ask

What is panresponder in React Native?

This is documentation for React Native 0.60, which is no longer actively maintained. For up-to-date documentation, see the latest version ( 0.66 ). PanResponder reconciles several touches into a single gesture. It makes single-touch gestures resilient to extra touches, and can be used to recognize basic multi-touch gestures.

What is panresponder in Android?

PanResponder reconciles several touches into a single gesture. It makes single-touch gestures resilient to extra touches, and can be used to recognize basic multi-touch gestures. By default, PanResponder holds an InteractionManager handle to block long-running JS events from interrupting active gestures.

How does panresponder work with gestures?

By default, PanResponder holds an InteractionManager handle to block long-running JS events from interrupting active gestures. It provides a predictable wrapper of the responder handlers provided by the gesture responder system. For each handler, it provides a new gestureState object alongside the native event object:

How do I use panresponder in rntester?

Try the PanResponder example in RNTester. The config object provides enhanced versions of all of the responder callbacks that provide not only the PressEvent, but also the PanResponder gesture state, by replacing the word Responder with PanResponder in each of the typical onResponder* callbacks. For example, the config object would look like:


3 Answers

Instead of letting your animation die at your borders, you could interpolate your Animated.Value with y=x, but with clamping it to your width.

return (
    <View style={styles.container}>
        <Animated.View 
            style={{
                transform: [{
                    translateX: this.state.pan.x.interpolate({
                        inputRange: [0, trackWidth ],
                        outputRange: [0, trackWidth ],
                        extrapolate: 'clamp'
                    })
                }],

            }} 
            {...this._panResponder.panHandlers}
        />
    </View>
);

Here's a more in-depth example: https://github.com/olapiv/expo-audio-player/blob/master/src/AudioSlider.js

like image 149
Beolap Avatar answered Oct 18 '22 20:10

Beolap


onPanResponderMove: (e, gestureState)=> {
    this.state.pan.x._value > 0 ? null : Animated.event([
            null, 
            {dx: this.state.pan.x, dy: this.state.pan.y},
        ])(e, gestureState)
    },
like image 5
Akshay Avatar answered Oct 18 '22 20:10

Akshay


I was trying to do something similar; I wanted to have it so that you can pull the page part way and then release and it goes back to where it was.

My solution was this:

panResponder = PanResponder.create({
  onMoveShouldSetPanResponderCapture: (e, { dx }) => {
    // This will make it so the gesture is ignored if it's only short (like a tap).
    // You could also use moveX to restrict the gesture to the sides of the screen.
    // Something like: moveX <= 50 || moveX >= screenWidth - 50
    // (See https://facebook.github.io/react-native/docs/panresponder)
    return Math.abs(dx) > 20;
  },
  onPanResponderMove: (e, gestureState) => (
    // Here, 30 is the limit it stops at. This works in both directions
    Math.abs(gestureState.dx) > 30
      ? null
      : Animated.event([null, { dx: this.animatedVal }])(e, gestureState)
  ),
  onPanResponderRelease: (e, { vx, dx }) => {
    // Here, abs(vx) is the current speed (not velocity) of the gesture,
    // and abs(dx) is the distance traveled (not displacement)
    if (Math.abs(vx) >= 0.5 || Math.abs(dx) >= 30) {
      doSomeAction();
    }
    Animated.spring(this.animatedVal, {
      toValue: 0,
      bounciness: 10,
    }).start();
  },
});
like image 3
dǝɥɔS ʇoıןןƎ Avatar answered Oct 18 '22 21:10

dǝɥɔS ʇoıןןƎ