手撸antdmobile的tabs组件

1,319 阅读2分钟

tab组件效果详见:https://mobile.ant.design/components/tabs

废话

作为前端应用工程师,我很理解小伙伴们在习惯了cv之后,找不到直接可以完整copy的代码的心情。于是我今天分享了这个js实现的tab切换,觉得写得不错的小伙伴可以随手点个👍

实现思路

1.首先,考虑该组件由哪些元素组成,tab文字内容由li标签处理,滑块由一个span标签处理。

2.点击滑块需要滑动,给滑块一个定位,改变left从0px===>npx,用transtion(过渡)实现动画效果。

3.现在考虑两个细节点

1⃣️ 滑块的宽度与tab文字宽度(即高亮tab的li标签的宽度)一致,那么可以通过ref获取到node节点,const { width } = node.getBoundingClientRect();取到节点的宽度,即确定滑块的宽度。

2⃣️ 切换tab时,滑块的靠左的距离为当前tab前n个Li标签宽度的总和加上tab之间的margin。

3⃣️ 移动端,tab过多需要再点击后面几个tab的时候将ul整体往左移(效果可见antdmobile中tab的超长自动滚动

import './App.css';
import React, { useEffect, useRef, useState } from "react"
const tabs = ["苹果", "橘子", "西瓜", "草莓", "香蕉"]

function App() {
  const [activeIndex, setActiveIndex] = useState(0);
  const [indicatorWidth, setIndicatorWidth] = useState(0);
  const scrollRef = useRef(null);
  const tabContainerRef = useRef(null);
  const [leftDistance, setLeftDistance] = useState(0);
  const [tabNodes, setTabNodes] = useState([]);
  const setInsWidth = (Nodes, currentIndex = 0) => {
    const currentTab = Nodes[currentIndex];
    const { width } = currentTab.getBoundingClientRect();
    setIndicatorWidth(width)
  }
  const setIndicatorX = (Nodes, currentIndex) => {
    let max = Math.max(currentIndex, 0);
    setInsWidth(Nodes, currentIndex)
    let leftDistance = 0;
    for (let i = 0; i < max; i++) {
      const dom = Nodes[i];
      const { width } = dom.getBoundingClientRect();
      leftDistance += width+30

    }
    setLeftDistance(leftDistance)
    scrollRef.current.scrollLeft = leftDistance - 50
    console.log(leftDistance)
  }
  const toggle = (i) => {
    setActiveIndex(i)
    setIndicatorX(tabNodes, i)
  }
  useEffect(() => {
    const tabList = [...tabContainerRef.current.children]
    setTabNodes(tabList)
    setIndicatorX(tabList, activeIndex)
  }, [])
  return (
    <div className="tab-warpper">
      <div ref={scrollRef}>
        <ul className="tab-list" ref={tabContainerRef}>
          {
            tabs.map((tab, i) => {
              return <li key={tab} className={activeIndex === i ? 'active' : ''} onClick={() => toggle(i)}>{tab}</li>
            })
          }
        </ul>
        <span className="indicator" style={{ width: indicatorWidth + 'px', left: leftDistance + 'px' }}></span>
      </div>
    </div>
  )
}
export default App;

/*
`App.css`
*/

*{
  margin:0;
  padding:0;
}

.tab-warpper>div{
  overflow-x: auto;
  position: relative;
}
.tab-warpper>div .indicator{
  position: absolute;
  display: inline-block;
  height: 2px;
  background-color: darkcyan;
  bottom: 0;
  transition: all .3s;
}
.tab-warpper>div::-webkit-scrollbar {
  display:none
}
.tab-list{
  white-space:nowrap;
}
.tab-list li{
  list-style: none;
  display: inline-block;
  border-right: 1px solid #fff;
  line-height: 24px;
  text-align: center;
  margin-right:30px;
}
.tab-list li.active{
  color: darkcyan;
}