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;
}