基于react native的简单手动切换功能示例

基于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;

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值