基于react native的自定义轮播图
效果示例图
示例代码
import React, {useEffect, useRef, useState} from 'react';
import {
Animated,
PanResponder,
StyleSheet,
Text,
View,
Dimensions,
} from 'react-native';
import {pxToPd} from '../../common/js/device';
const TestSwiper = () => {
//动态获取的值
const [tempList, setTempList] = useState([
{id: 1},
{id: 2},
{id: 3},
{id: 4},
]);
const [swiperList, setSwiperList] = useState([]);
const swiperListRef = useRef([]);
//定时器手柄
const intervalHandleRef = useRef(null);
//手势滑动区域节点
const animatedViewRef = useRef(null);
//单个切换页面的宽度
const deviceWidth = Dimensions.get('window').width;
// 默认显示下标的页面
let currentIndexRef = useRef(0);
const panResponderEnabled = useRef(true);
//滑动的距离
const defaultMove = -currentIndexRef.current * deviceWidth;
const pan = useRef(new Animated.Value(defaultMove)).current;
//手势操作
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
clearInterval(intervalHandleRef.current); // 暂停自动轮播
panResponderEnabled.current = 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;
}
pan.setValue(moveX + gestureState.dx);
if (panResponderEnabled.current) {
panResponderEnabled.current = false; // 防止多次暂停自动轮播
clearInterval(intervalHandleRef.current); // 暂停自动轮播
}
},
//处理手势释放时的逻辑
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;
Animated.timing(pan, {
toValue: moveX,
duration: 300,
useNativeDriver: true,
}).start(() => {
if (newPageIndex == count - 1) {
currentIndexRef.current = 0;
pan.setValue(0);
}
autoPlayAPI(); // 继续自动轮播
});
} else {
pan.setValue(-currentIndexRef.current * deviceWidth);
}
if (!panResponderEnabled.current) {
autoPlayAPI(); // 继续自动轮播
}
},
}),
).current;
//自动轮播
const autoPlayAPI = () => {
const max = swiperListRef.current.length - 1;
if (intervalHandleRef.current) {
clearInterval(intervalHandleRef.current);
}
intervalHandleRef.current = setInterval(() => {
let newPageIndex = 0;
if (currentIndexRef.current == max) {
newPageIndex = 0;
} else {
newPageIndex = currentIndexRef.current + 1;
}
const moveX = -newPageIndex * deviceWidth;
currentIndexRef.current = newPageIndex;
Animated.timing(pan, {
toValue: moveX,
duration: 300,
useNativeDriver: true,
}).start(() => {
if (newPageIndex == max) {
currentIndexRef.current = 0;
pan.setValue(0);
autoPlayAPI();
}
});
}, 3000);
};
//初始化
const initFunction = () => {
let tempArr = [...tempList];
let firstArr = tempArr[0];
let contactArr = tempArr.concat(firstArr);
swiperListRef.current = contactArr;
setSwiperList(() => contactArr);
autoPlayAPI();
};
useEffect(() => {
initFunction();
return () => {
clearInterval(intervalHandleRef.current);
};
}, []);
return (
<>
<View style={styles.swiperWrap}>
<Animated.View
ref={animatedViewRef}
style={{
width: deviceWidth * swiperList.length,
flex: 1,
flexDirection: 'row',
transform: [{translateX: pan}],
onStartShouldSetResponderCapture: () => false, // 禁止拦截触摸事件
}}
{...panResponder.panHandlers}>
{swiperList.map((item, index) => (
<View key={'swiperItem' + index} style={{width: deviceWidth}}>
<View style={styles.swiperItem}>
<Text>item {item.id}</Text>
</View>
</View>
))}
</Animated.View>
</View>
</>
);
};
const styles = StyleSheet.create({
swiperWrap: {
borderColor: 'red',
borderWidth: pxToPd(1),
borderStyle: 'solid',
width: '100%',
height: pxToPd(400),
},
swiperItem: {
borderColor: 'red',
borderWidth: pxToPd(1),
borderStyle: 'solid',
borderRadius: pxToPd(12),
width: '93.6%',
marginLeft: '3.2%',
height: '100%',
},
});
export default TestSwiper;