限制 PanResponder 移动 React Native

Joe*_*ugh 5 react-native

这个问题与

我正在尝试使用 PanResponder 构建一个水平滑块。我可以使用以下代码在 x 轴上移动元素,但我想限制可以移动它的范围。

这是一个带注释的示例:

export class MySlider extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            pan: new Animated.ValueXY()
        };
        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder : () => true,
            onPanResponderGrant: (e, gestureState) => {
                this.state.pan.setOffset(this.state.pan.__getValue());
                this.setState({isAddNewSession:true});
            },
                ///////////////////////////
                // CODE OF INTEREST BELOW HERE
                ///////////////////////////
            onPanResponderMove: (evt, gestureState) => {
                // I need this space to do some other functions
                // This is where I imagine I should implement constraint logic
                return Animated.event([null, {
                    dx: this.state.pan.x
                }])(evt, gestureState)
            },
            onPanResponderRelease: (e, gesture) => { 
                this.setState({isAddNewSessionModal:true});
                this.setState({isAddNewSession:false});
            }
        });
    render() {
        let { pan } = this.state;
        let translateX = pan.x;
        const styles = StyleSheet.create({
            pan: {
                transform: [{translateX:translateX}]
            },
            slider: {
                height: 44,
                width: 60,
                backgroundColor: '#b4b4b4'
            },
            holder: {
                height: 60,
                width: Dimensions.get('window').width,
                flexDirection: 'row',
                backgroundColor: 'transparent',
                justifyContent: 'space-between',
                borderStyle: 'solid',
                borderWidth: 8,
                borderColor: '#d2d2d2'
            }
        });
        const width = Dimensions.get('window').width - 70
        return (
            <View style={styles.holder}>
            <Animated.View 
                hitSlop={{ top: 16, left: 16, right: 16, bottom: 16 }} 
                style={[styles.pan, styles.slider]} 
                {...this._panResponder.panHandlers}/>           
            </View>
            )
    }
}
Run Code Online (Sandbox Code Playgroud)

为了限制值使其不能低于 0,我尝试实现 if else 逻辑,如下所示:

        onPanResponderMove: (evt, gestureState) => {
            return (gestureState.dx > 0) ? Animated.event([null, {
                dx: this.state.pan1.x
            }])(evt, gestureState) : null
        },
Run Code Online (Sandbox Code Playgroud)

但这有问题 - 它最初似乎有效,但最小 x 限制似乎有效增加。我向后和向前滚动得越多,最小 x 限制似乎就会增加。

我也试过这个:

        onPanResponderMove: (evt, gestureState) => {
            return (this.state.pan1.x.__getValue() > 0) ? Animated.event([null, {
                dx: this.state.pan1.x
            }])(evt, gestureState) : null
        },
Run Code Online (Sandbox Code Playgroud)

但它似乎根本不起作用。

如何将检测到的手指移动的整个宽度插入到我定义的有限范围内?

小智 8

gestureState.dx是用户每次滑动时手指从其原始位置移动的差异。因此,只要用户抬起手指,它就会重置,这会导致您的问题。

有几种方法可以限制该值:

使用插值

let translateX = pan.x.interpolate({inputRange:[0,100],outputRange:[0,100],extrapolateLeft:"clamp"}) 虽然这有效,但用户向左滑动的次数越多,他必须向右滑动的次数就越多才能到达“真正的 0”

释放时重置值

onPanResponderRelease: (e, gestureState)=>{
        this.state.pan.setValue({x:realPosition<0?0:realPosition.x,y:realPosition.y})

}
Run Code Online (Sandbox Code Playgroud)

确保您使用获取当前值this.state.pan.addListener并将其放入realPosition 您可以允许向左滑动并在某种弹簧中将其动画化回来,或者只是使用以前的插值方法防止它完全消失。

但您应该考虑使用其他东西,因为 PanResponder 不支持 useNativeDriver。要么使用scrollView(如果你想要4方向滚动,则使用两个),它通过其内容或类似wix的内容来限制滚动react-native-interactable


Vin*_*k B 0

这是针对您的问题的解决方案,或者您可以使用它作为替代解决方案。我没有pan在此解决方案中使用。这个想法是,限制滑块在父视图内的移动。所以它不会转移到父级。考虑下面的代码

 export default class MySlider extends Component<Props> {
  constructor(props) {
    super(props);
    this.containerBounds={
      width:0
    }
    this.touchStart=8;
    this.sliderWidth= 60;
    this.containerBorderWidth=8
    this.state = {
      frameStart:0
    };

    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderGrant: (e, gestureState) => {
        this.touchStart=this.state.frameStart;
        this.setState({ isAddNewSession: true });
      },

      onPanResponderMove: (evt, gestureState) => {
        frameStart = this.touchStart + gestureState.dx;
        if(frameStart<0){    
          frameStart=0
        }else if(frameStart+this.sliderWidth>this.containerBounds.width-2*this.containerBorderWidth){             
          frameStart=this.containerBounds.width-this.sliderWidth-2*this.containerBorderWidth
        }

        this.setState({
          frameStart:frameStart
        })

      },
      onPanResponderRelease: (e, gesture) => {
        this.setState({ isAddNewSessionModal: true });
        this.setState({ isAddNewSession: false });
      }
    });

  }
    render() {

      const styles = StyleSheet.create({

        slider: {
          height: 44,
          width:  this.sliderWidth,
          backgroundColor: '#b4b4b4'
        },
        holder: {
          height: 60,
          width: Dimensions.get('window').width,
          flexDirection: 'row',
          backgroundColor: 'transparent',
          justifyContent: 'space-between',
          borderStyle: 'solid',
          borderWidth: this.containerBorderWidth,
          borderColor: '#d2d2d2'
        }
      });
      return (
        <View style={styles.holder}
        onLayout={event => {
          const layout = event.nativeEvent.layout;
          this.containerBounds.width=layout.width;
      }}>
          <Animated.View
            hitSlop={{ top: 16, left: 16, right: 16, bottom: 16 }}
            style={[{left:this.state.frameStart}, styles.slider]}
            {...this._panResponder.panHandlers} />
        </View>
      )
    }
  }
Run Code Online (Sandbox Code Playgroud)