我正在尝试使用本机PanResponder
创建一个自定义水平范围滑块
在用户平移时,我用Animated.View
值更新了Animated.ValueXY
,当用户点击左边框或右边框时,我想以某种方式停止移动此Animated.View
,但是当用户滑动时该视图不会在边界上停留得更快,并且超出了限制。
我该如何解决?
这是完整的代码,
import React, { useRef, useEffect } from 'react'
import {
Platform,
View,
PanResponder,
Animated,
Dimensions,
StyleSheet
} from 'react-native';
import PropTypes from 'prop-types';
function usePanResponders({ parentWidth, reverse, width, values, onMoving}) {
const endXPos = parentWidth - width;
const _values = useRef({...values}).current;
const pan = useRef(new Animated.ValueXY({...values})).current
useEffect(() => {
pan.addListener((values) => {
_values.x = values.x
});
return () => {
pan.removeAllListeners();
}
}, [])
return [useRef(PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: (_, { dx }) => Math.abs(dx) > 20,
onPanResponderGrant: () => {
if(!reverse) {
console.log(reverse)
pan.setOffset({x: _values.x, y: _values.y});
pan.setValue({x: 0, y: 0});
}
},
onPanResponderMove: (e, gestureState) => {
const isMovingLeft = gestureState.dx > 0;
// this is how i stops right border
if((_values.x >= endXPos) && isMovingLeft) return null;
// left border
if(!isMovingLeft && _values.x <= 0) return null;
return Animated.event(
[ null,
{dx: pan.x}
],
{listener: null}
)(e, gestureState);
},
onPanResponderRelease: () => onMoving(_values)
})).current, pan];
}
function useCss({width, height, color}) {
return [
Platform.select({
ios: {
zIndex: 999,
position: 'absolute',
},
android: {
position: 'absolute',
}
}),
{
backgroundColor: color,
width,
height
}
]
}
function AsyncDraggable({x, y, width, height, color, parentWidth, reverse, onDragEnd}) {
const [position, item] = useCss({width, height, color})
const onMoving = (v) => {
console.log(v)
}
const values = {x, y}
console.log('Rendering...')
const [panresponder, pan] = usePanResponders({parentWidth, reverse, width, pan, values, onMoving})
return (
<View style={{...position}}>
<Animated.View
{...panresponder.panHandlers}
style={[pan.getLayout()]}>
<View
style={{...item}}
>
</View>
</Animated.View>
</View>
)
}
AsyncDraggable.prototype = {
width: PropTypes.number,
height: PropTypes.number,
color: PropTypes.string,
parentWidth:PropTypes.number.isRequired,
onDragEnd: PropTypes.func,
onMove: PropTypes.func,
x:PropTypes.number,
y:PropTypes.number
}
AsyncDraggable.defaultProps = {
x : 0,
y : 0,
reverse : false,
onMove: () => {}
}
const margin = {
top: 10,
bottom: 20,
left: 50,
right: 15
};
const screenWidth = Dimensions.get('window').width;
function Scroller({width, height, panWidth}){
const computedWidth = width - (margin.left + margin.right);
const _height = height || 70
return (
<View style={{
marginLeft: margin.left,
marginRight: margin.right,
marginTop: margin.top,
width: computedWidth,
overflow: 'hidden',
height,
backgroundColor: 'rgba(255,255,255,0.04)'}}>
<AsyncDraggable x={10} color={'yellow'} parentWidth={computedWidth} width={panWidth} height={_height}/>
</View>
)
}
Scroller.prototype = {
height: PropTypes.number,
width: PropTypes.number,
}
Scroller.defaultProps = {
height: 70,
width: screenWidth,
panWidth: 60
}
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Scroller/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 8
}
});
如代码所示
if((_values.x >= endXPos) && isMovingLeft) return null;
if(!isMovingLeft && _values.x <= 0) return null;
这就是我设法阻止左右移动的方式。但是当用户快速移动时它没用。