基于react native的自定义轮播图

基于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;
相关推荐
Q_w774231 分钟前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录
昨天;明天。今天。42 分钟前
案例-任务清单
前端·javascript·css
一丝晨光1 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思1 小时前
vue使用高德地图
javascript·vue.js·ecmascript
zqx_72 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己2 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2343 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦3 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http
神之王楠4 小时前
如何通过js加载css和html
javascript·css·html