【问题标题】:ReactNative PanResponder limit X positionReact Native PanResponder 限制 X 位置
【发布时间】:2017-09-13 03:44:37
【问题描述】:

我正在构建一个音乐播放器,我专注于进度条。 我能够对滑动手势做出反应,但我无法限制该手势的移动距离。

这是我到目前为止所做的。我已将所有内容减少到最低限度:

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 &lt; 0,但它不起作用,因为它似乎是一个偏移量,而不是一个位置。

任何帮助将不胜感激。

【问题讨论】:

    标签: android react-native gestures


    【解决方案1】:

    你可以插入你的动画,而不是让你的动画死在你的边界上 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

    【讨论】:

      【解决方案2】:
      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)
          },
      

      【讨论】:

      • 这种做法的问题是,当达到极限时,不能再往另一个方向移动,因为this.state.pan被设置为null。
      【解决方案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();
        },
      });
      

      【讨论】:

        【解决方案4】:

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

        希望这可以帮助一些人。 ?

        【讨论】:

          【解决方案5】:

          一个棘手的问题是,虽然我无法将图标移动到该钳位之外,但 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? 学到的

          【讨论】:

            【解决方案6】:

            在另一个问题上有类似问题的解决方案: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)
               }
             },
            

            这不是我的答案,所以我建议您点击链接以查看进一步的解释,如果您喜欢它,请为原始海报投票! :) 我试图做同样的事情,它对我有用!希望对您有所帮助。

            附:我发现其他依赖检查动画值而不是手势值的解决方案有时会卡住。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-14
              • 2018-02-07
              • 1970-01-01
              • 2017-01-14
              • 1970-01-01
              相关资源
              最近更新 更多