巧用Scrollview实现Tab组件动态切换

巧用Scrollview实现Tab组件动态切换

实现效果:
不仅能左右滑动,同时还能够在点击的时候自动滑动,将点击的位置滑动到正中间。

代码实现

import React from 'react'
import { Dimensions, ScrollView, Text, TouchableOpacity, View, StyleSheet } from 'react-native'
import LinearGradient from 'react-native-linear-gradient'

const { width } = Dimensions.get('window');
const deviceWidth = width;
const tabs = ['北京', '上海', '深圳', '呼和浩特', '南京', '哈尔滨', '乌鲁木齐', '广州'];

⚠️该类组件不能继承PureComponent,当继承PureComponent时可能导致进入时第一个选项不会显示下面的渐变线

export default class ScrollTab extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      curIndex: 0,
      linearGradientWidth: []
    }
    this.scroll = null;
    this.laout_list = [];
    this.scrollW = 0;
  }
  render() {
    return (
      <ScrollView
        ref={e => this.scroll = e}
        horizontal
        directionalLockEnabled
        showsHorizontalScrollIndicator={false}
        // snapToAlignment会定义停驻点与滚动视图之间的关系
        // start (默认) 会将停驻点对齐在左侧(水平)或顶部(垂直)
        // center 会将停驻点对齐到中间
        // end 会将停驻点对齐到右侧(水平)或底部(垂直)  
        snapToAlignment="center"
      >
        {/* 具体需要展示的样式 */}
        {
          tabs.map((item, index) => {
            return (
              <TouchableOpacity
                onPress={() => this.setIndex(index)}
                // onLayout属性获取当前组件的位置和属性信息{width,height,x,y}
                onLayout={e => this.setlaout(e.nativeEvent.layout, index)}
                key={item}
                style={styles.tabItem}
              >
                <View>
                  <Text style={styles.tabWord} onLayout={({ nativeEvent: e }) => this.handleTextLength({ e, index })}>{item}</Text>
                  {this.linearTab(index)}
                </View>
              </TouchableOpacity>
            )
          })
        }
      </ScrollView>
    )
  }
setlaout(layout, index) {
    this.laout_list[index] = layout;
    // 统计总宽度
    this.scrollW += layout.width;
  }
  // 核心: 需要滑动的位置=点击位置的左边距-APP屏幕/2+点击位置的宽度/2
  setIndex(index, bl = true) {
    if (this.state.curIndex !== index) {
      this.setState({
        curIndex: index
      })
    }
    if (!this.scroll) return;
    let layout = this.laout_list[index];
    let rx = deviceWidth / 2;
    let sx = layout.x - rx + layout.width / 2;
    // 不满足滑动条件,停留不动
    if (sx < 0) sx = 0;
    // 正常滑动
    sx < this.scrollW - deviceWidth && this.scroll.scrollTo({ x: sx, animated: bl });
    // 靠后部分则直接滑到底部
    sx >= this.scrollW - deviceWidth && this.scroll.scrollToEnd({ animated: bl });
  }
  // 设置文字下面的线的长度
  handleTextLength = ({ e, index }) => {
    const { linearGradientWidth } = this.state;
    linearGradientWidth[index] = e.layout.width;
    this.setState({
      linearGradientWidth
    })
  }
  linearTab = (index) => {
    const { curIndex, linearGradientWidth } = this.state;
    if (curIndex == index) {
      return (
        <View style={styles.linearGradient}>
          <LinearGradient
            start={{ x: 0, y: 0 }}
            end={{ x: 1, y: 0 }}
            colors={['skyblue', 'blue']}
            style={{
              height: 10,
              width: linearGradientWidth[index]
            }}
          />
        </View>
      )
    }
    return null
  }
const styles = StyleSheet.create({
  tabItem: {
    marginRight: 10
  },
  tabWord: {
    color: ' #333',
    fontSize: 24,
    paddingVertical: 10
  },
  linearGradient: {
    position: 'absolute',
    left: 0,
    bottom: -1,
    borderTopLeftRadius: 4,
    borderTopRightRadius: 4
  }
})

本文主体思路摘自https://www.jb51.net/article/143344.htm

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值