简体   繁体   English

ReactNative PanResponder 限制 X 位置

[英]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:初始位置:

初始位置

Wrong Position, limit exceeded:位置错误,超出限制:

错误的位置

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.我尝试检查是否_value < 0 ,但它没有用,因为它似乎是一个偏移量,而不是一个位置。

Well any help will be appreciated.那么任何帮助将不胜感激。

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

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.您可以使用 y=x 插入您的 Animated.Value,而不是让您的动画在边界处消失,但将其限制在您的宽度上。

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这是一个更深入的示例: https : //github.com/olapiv/expo-audio-player/blob/master/src/AudioSlider.js

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

🍰 This method doesn't cancel the gesture handler if the user is still holding it down after exceeding the X limit. 🍰 如果用户在超过 X 限制后仍然按住它,则此方法不会取消手势处理程序。 Change MaxDistance & MinDistance to whatever values you like 😃将 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);
},

Hope this helps some folks.希望这可以帮助一些人。 🐱 🐱

A tricky point is that while I can not move the icon beyond that clamp but the pan.x value is indeed beyond the clamp limit although you don't see it.一个棘手的问题是,虽然我无法将图标移到该钳位之外,但 pan.x 值确实超出了钳位限制,尽管您看不到它。 Then, when you want to move it back, you don't know how much swipe you need to move it back.然后,当您想将其移回时,您不知道需要多少次滑动才能将其移回。 This could be a nuance.这可能是一个细微差别。

My solution is:我的解决办法是:

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

Then, can also console.log pan.x._value in following to double check.然后,也可以在下面的 console.log pan.x._value 中仔细检查。

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

I found this helpful for my own project.我发现这对我自己的项目很有帮助。 Note can only use pan.x_value not pan.x.注意只能使用 pan.x_value 不能使用 pan.x。 In my case I also used useMemo instead of useRef so the limits can be reset, which I learned from React Native's panResponder has stale value from useState?在我的情况下,我还使用了 useMemo 而不是 useRef,因此可以重置限制,这是我从React Native 的 panResponder中学到的,它具有来自 useState 的陈旧值?

There's a solution to a similar problem on this other question: https://stackoverflow.com/a/58886455/9021210在另一个问题上有一个类似问题的解决方案: https : //stackoverflow.com/a/58886455/9021210

that can be repurposed for what you're looking for可以重新用于您正在寻找的内容

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

This is not my answer, so I recommend you follow the link to see further explanation and if you like it upvote the original poster!这不是我的答案,所以我建议您点击链接以查看进一步的解释,如果您喜欢它,请为原始海报点赞! :) I was trying to do the same thing and it worked for me! :) 我试图做同样的事情,它对我有用! hope it helps.希望它有帮助。

PS I found that other solutions relying on checking the animation value rather than the gesture value would sometimes get stuck. PS我发现依赖检查动画值而不是手势值的其他解决方案有时会卡住。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM