这个问题与
我正在尝试使用 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。
这是针对您的问题的解决方案,或者您可以使用它作为替代解决方案。我没有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)
| 归档时间: |
|
| 查看次数: |
4536 次 |
| 最近记录: |