基于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;
相关推荐
噢,我明白了3 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__3 小时前
APIs-day2
javascript·css·css3
关你西红柿子3 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根3 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.4 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Aphasia3114 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
m0_748256564 小时前
Vue - axios的使用
前端·javascript·vue.js
m0_748256344 小时前
QWebChannel实现与JS的交互
java·javascript·交互
胡西风_foxww4 小时前
【es6复习笔记】函数参数的默认值(6)
javascript·笔记·es6·参数·函数·默认值
胡西风_foxww4 小时前
【es6复习笔记】生成器(11)
javascript·笔记·es6·实例·生成器·函数·gen