ReactNative PanResponder limit X позиция

Я создаю музыкальный проигрыватель и сосредотачиваюсь на индикаторе выполнения. Я мог реагировать на жесты смахивания, но не могу ограничить, насколько далеко заходит этот жест.

Это то, что я делал до сих пор. Я свел все к минимуму:

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>
    );
}

Вот изображение, показывающее, в чем проблема.

Исходное положение:

«Исходное

Неверная позиция, превышен лимит:

«Неправильная

Таким образом, идея состоит в том, чтобы прекратить движение после достижения предела (как влево, так и вправо). Я попытался проверить, _value < 0, но это не сработало, так как это похоже на смещение, а не на позицию.

Любая помощь будет оценена по достоинству.


person Facundo La Rocca    schedule 13.09.2017    source источник


Ответы (6)


Вместо того, чтобы позволить вашей анимации умирать на ваших границах, вы можете интерполировать свой Animated.Value с y = x, но с ограничением его ширины.

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>
);

Вот более подробный пример: https://github.com/olapiv/expo-audio-player/blob/master/src/AudioSlider.js.

person Beolap    schedule 10.10.2019

Я пытался сделать что-то подобное; Я хотел, чтобы вы могли частично потянуть страницу, а затем отпустить, и она вернется на место.

Мое решение было таким:

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();
  },
});
person Chicken Suop    schedule 31.08.2018

???? Этот метод не отменяет обработчик жестов, если пользователь все еще удерживает его после превышения предела X. Измените MaxDistance и MinDistance на любые значения, которые вам нравятся ????

onPanResponderMove: (e, gestureState) => {
  // Configure Min and Max Values
  const MaxDistance = maxDistance;
  const MinDistance = 0;
  const dxCapped = Math.min(Math.max(parseInt(gestureState.dx), MinDistance), MaxDistance);

  // If within our bounds, use our gesture.dx....else use dxCapped
  const values = {}
  if(gestureState.dx < MaxDistance && gestureState.dx > MinDistance){
    values.dx = gestureState.dx
    values.dy = gestureState.dy
  }else{
    values.dx = dxCapped
    values.dy = gestureState.dy
  }

  //Animate Event
  Animated.event([null, {
    dx: pan.x,
    dy: pan.y,
  }])(e, values);
},

Надеюсь, это поможет некоторым людям. ????

person Zurdge    schedule 11.06.2020

Сложность заключается в том, что, хотя я не могу переместить значок за пределы этого зажима, значение pan.x действительно выходит за пределы ограничения, хотя вы его не видите. Затем, когда вы хотите переместить его обратно, вы не знаете, сколько смахивания вам нужно, чтобы переместить его обратно. Это может быть нюанс.

Мое решение:

      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,
        });
      },

Затем можно также выполнить console.log pan.x._value для двойной проверки.

      onPanResponderRelease: () => {
        pan.flattenOffset();}

Я нашел это полезным для моего собственного проекта. Примечание может использовать только pan.x_value, но не pan.x. В моем случае я также использовал useMemo вместо useRef, чтобы ограничения можно было сбросить, о чем я узнал из PanResponder React Native имеет устаревшее значение из useState?

person letmeask    schedule 03.03.2021

Есть решение аналогичной проблемы по этому другому вопросу: https://stackoverflow.com/a/58886455/9021210

которые можно использовать для того, что вы ищете

const DRAG_THRESHOLD = /*configure min value*/
const DRAG_LIMIT = /*configure max value*/

onPanResponderMove: (e, gesture) => {
   if ( (Math.abs( gesture.dy ) > DRAG_THRESHOLD) && 
        (Math.abs( gesture.dy ) < DRAG_LIMIT ) )
   {
       return Animated.event([
           null, {dx: 0, dy: pan.y}
       ]) (e, gesture)
   }
 },

Это не мой ответ, поэтому я рекомендую вам перейти по ссылке, чтобы увидеть дальнейшие объяснения, и, если вам это нравится, проголосуйте за оригинальный плакат! :) Я пытался сделать то же самое, и у меня это сработало! Надеюсь, это поможет.

P.S. Я обнаружил, что другие решения, основанные на проверке значения анимации, а не значения жеста, иногда застревают.

person Brooklin Myers    schedule 25.06.2020

person    schedule
comment
Проблема с этим подходом заключается в том, что при достижении предела вы не можете снова двигаться в другом направлении, потому что this.state.pan был установлен как null. - person Yulio Aleman Jimenez; 07.10.2020