问题
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.
Well any help will be appreciated.
回答1:
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)
},
回答2:
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();
},
});
回答3:
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.
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://stackoverflow.com/questions/46188512/reactnative-panresponder-limit-x-position