基于react native的简单手动切换功能示例
代码示例
import React, {useEffect, useRef, useState} from 'react';
import {
Animated,
PanResponder,
StyleSheet,
Text,
View,
Dimensions,
ScrollView,
} from 'react-native';
import {pxToPd} from '../../common/js/device';
import MatchTab from './matchTab';
const TestExampleTab = ({navigation}) => {
const [tabCode, setTabCode] = useState(2);
const [tabList, setTabList] = useState([
{
name: '报名中',
code: 2,
},
{
name: '进行中',
code: 3,
},
{
name: '已结束',
code: 8,
},
{
name: '已作废',
code: 11,
},
]);
//手势滑动区域节点
const animatedViewRef = useRef(null);
//单个切换页面的宽度
const deviceWidth = Dimensions.get('window').width;
// 默认显示下标的页面
let currentIndexRef = useRef(0);
//滑动的距离
const defaultMove = -currentIndexRef.current * deviceWidth;
const pan = useRef(new Animated.Value(defaultMove)).current;
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
//处理手势移动事件,其中使用了`dx`参数来表示在x轴上的移动距离
onPanResponderMove: (evt, gestureState) => {
//获取当前滚动区域有几个孩子节点
const count = animatedViewRef.current._children.length;
//每次移动的距离
const moveX = -currentIndexRef.current * deviceWidth;
//当移动到最左侧或者最右侧时,禁止拖动
const start = currentIndexRef.current == 0 && gestureState.dx > 0;
const end = currentIndexRef.current == count - 1 && gestureState.dx < 0;
if (start || end) {
// 禁止继续拖动
return false;
}
if (Math.abs(gestureState.dx) < 50) {
return false;
}
pan.setValue(moveX + gestureState.dx);
},
//处理手势释放时的逻辑
onPanResponderRelease: (_, gestureState) => {
//获取当前滚动区域有几个孩子节点
const count = animatedViewRef.current._children.length;
//当手指拖动区域大于100的时候,开始切换页面
if (Math.abs(gestureState.dx) > 100) {
let newPageIndex = currentIndexRef.current;
if (gestureState.dx > 0) {
newPageIndex = Math.max(0, currentIndexRef.current - 1);
} else {
newPageIndex = Math.min(count - 1, currentIndexRef.current + 1);
}
const moveX = -newPageIndex * deviceWidth;
currentIndexRef.current = newPageIndex;
pan.setValue(moveX);
} else {
pan.setValue(-currentIndexRef.current * deviceWidth);
}
setTabCode(() => tabList[currentIndexRef.current].code);
},
}),
).current;
//tab切换
const tabChangeHandle = row => {
setTabCode(() => row.status);
let subscript = tabList.findIndex(item => item.code == row.status);
currentIndexRef.current = subscript;
pan.setValue(-subscript * deviceWidth);
};
useEffect(() => {
return () => {};
}, []);
return (
<>
{/* tab */}
<View style={styles.tabWrap}>
<MatchTab list={tabList} code={tabCode} onClick={tabChangeHandle} />
</View>
<View style={styles.wraper}>
<Animated.View
ref={animatedViewRef}
style={{
width: deviceWidth * tabList.length,
flex: 1,
flexDirection: 'row',
transform: [{translateX: pan}],
onStartShouldSetResponderCapture: () => false, // 禁止拦截触摸事件
}}
{...panResponder.panHandlers}>
<View style={{width: deviceWidth, backgroundColor: 'red'}}>
<Text>item One</Text>
</View>
<View style={{width: deviceWidth}}>
<Text>item Two</Text>
</View>
<View style={{width: deviceWidth, backgroundColor: 'green'}}>
<Text>item Three</Text>
</View>
<View style={{width: deviceWidth}}>
<Text>item Four</Text>
</View>
</Animated.View>
</View>
</>
);
};
const styles = StyleSheet.create({
// tab
tabWrap: {
width: '100%',
height: pxToPd(80),
},
wraper: {
flex: 1,
backgroundColor: '#fff',
},
});
export default TestExampleTab;
matchTab
import React, {useRef, useState} from 'react';
import {
Image,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {pxToPd} from '../../common/js/device';
const MatchTabItem = ({item, current, onClick}) => {
const tabItemClickHandle = row => {
onClick && onClick(row);
};
return (
<>
<TouchableOpacity
style={styles.tabItem}
onPress={() => {
tabItemClickHandle(item);
}}>
{item.code == current ? (
<>
<Text style={styles.tabItemNameCurrent}>{item.name}</Text>
<View style={styles.tabLine}></View>
</>
) : (
<>
<Text style={styles.tabItemName}>{item.name}</Text>
</>
)}
</TouchableOpacity>
</>
);
};
const MatchTab = ({onClick, list = [], code = 2}) => {
//点击tab切换
const tabClickHandle = row => {
onClick &&
onClick({
type: 'status',
status: row.code,
});
};
return (
<>
<View style={styles.matchTab}>
<View style={styles.matchTabMenu}>
<ScrollView horizontal={true}>
{list.map(item => (
<MatchTabItem
key={item.code}
item={item}
current={code}
onClick={tabClickHandle}
/>
))}
</ScrollView>
</View>
</View>
</>
);
};
const styles = StyleSheet.create({
matchTab: {
width: '93.6%',
marginLeft: '3.2%',
flexDirection: 'row',
},
matchTabMenu: {
flex: 1,
},
matchTabFilter: {
width: pxToPd(120),
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
},
filterIcon: {
width: pxToPd(32),
height: pxToPd(32),
},
filterText: {
fontSize: pxToPd(26),
fontWeight: '400',
color: '#333',
marginLeft: pxToPd(10),
},
filterTextok: {
fontSize: pxToPd(26),
fontWeight: '400',
color: '#FFA157',
marginLeft: pxToPd(10),
},
tabItem: {
height: pxToPd(70),
position: 'relative',
alignItems: 'center',
justifyContent: 'center',
marginRight: pxToPd(40),
},
tabItemName: {
fontSize: pxToPd(26),
fontWeight: '400',
color: '#333',
},
tabItemNameCurrent: {
fontSize: pxToPd(30),
fontWeight: '500',
color: '#ff9f50',
},
tabLine: {
width: pxToPd(32),
height: pxToPd(6),
backgroundColor: '#ffa157',
borderRadius: pxToPd(3),
position: 'absolute',
left: '50%',
marginLeft: -pxToPd(16),
bottom: 0,
},
});
export default MatchTab;