ReactNative手势响应器限制X轴位置

13
我正在建造一款音乐播放器,专注于进度条。 我能够对滑动手势做出反应,但我不能限制手势的范围。
这是我到目前为止所做的。我将一切都简化到最小:
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>
    );
}

这里有一张图片展示了问题所在。

初始位置:

Initial position

错误位置,达到限制:

Wrong position

因此,想法是一旦到达限制(左侧和右侧)就停止移动。我尝试检查 _value < 0,但它没有起作用,因为它似乎是一个偏移量,而不是一个位置。

任何帮助将不胜感激。

6个回答

13

不要让你的动画在边界处消失,你可以将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


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

1
这种方法的问题在于当到达极限时,无法再向另一个方向移动,因为 this.state.pan 已设置为 null。 - Yulio Aleman Jimenez

3

我试图做类似的事情;我希望页面可以被部分拉动,然后释放后返回原来的位置。

我的解决方案如下:

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

2

如果用户在超过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);
},

希望这能帮助一些人。

1
一个棘手的问题是,虽然我不能将图标移动到限制范围之外,但 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,以便可以重置限制,这是我从React Native's panResponder has stale value from useState?中学到的。


0

在另一个问题中有一个类似问题的解决方案:https://dev59.com/-bDla4cB1Zd3GeqP6VEP#58886455

可以重新利用它来解决你正在寻找的问题。

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. 我发现其他依赖于检查动画值而不是手势值的解决方案有时会卡住。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接