手写tabs组件,功能包括:
- 功能包括点击tab自动回到可视区
- 底部横线滑动效果
- 下面的content内容没有实现,但是位置已经留出 js代码
import { useLayoutEffect, useState } from 'react';
import './index.less';
import classNames from "classnames";
const Tbs = ({ items = [] }) => {
const [activeTab, setActiveTab] = useState(1)
const lineDom = document.getElementsByClassName('tab-line')
const changeWidthLeft = (key) => {
// 实现滑动到可视区---start
// inline: 'nearest', block: 'nearest' 是为了方式页面元素超过高度时,点击会产生上下滚动
// https://www.51cto.com/article/767006.html
const currentDom = document.getElementById(key)
currentDom.scrollIntoView({ behavior: 'smooth', inline: 'nearest', block: 'nearest' });
// 实现滑动到可视区---end
const currentStyles = currentDom.getBoundingClientRect()
lineDom[0].style.width = currentStyles.width + 'px'
lineDom[0].style.left = currentDom.offsetLeft + 'px'
}
const clickTab = (item, index) => {
changeWidthLeft(item.key)
if (index !== activeTab) {
console.log(item)
setActiveTab(index)
}
}
useLayoutEffect(() => {
changeWidthLeft(items[activeTab].key)
}, [])
return (
<div className="wrapper">
<div className="tabs-header">
<div className={classNames('tabs-header-mask', 'tabs-header-mask-left')} />
<div className="tabs-header-mask tabs-header-mask-right" />
<div className="tabs-wrapper">
<div className="tab-texts" scrollIntoView={items[activeTab].key}>
{items.map((item, index) => {
return (
<div
className={classNames("tab-item", { 'tab-item-active': activeTab === index })}
id={item.key}
key={item.key}
onClick={() => clickTab(item, index)}
>
{item.label}
</div>
)
})}
<div id='tab-line' className='tab-line' />
</div>
</div>
</div>
</div>
)
}
export default Tbs
css代码
@basePadding: 8px;
.wrapper {
width: 100%;
.tabs-header {
position: relative;
.tabs-header-mask {
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
width: 30px;
height: 100%;
pointer-events: none;
}
.tabs-header-mask-left {
left: 0;
background: linear-gradient(90deg, #fff, hsla(0, 0%, 100%, 0));
}
.tabs-header-mask-right {
right: 0;
background: linear-gradient(270deg, #fff, hsla(0, 0%, 100%, 0));
}
.tabs-wrapper {
.tab-texts {
cursor: pointer;
background-color: #fff;
white-space: nowrap;
height: 40px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
position: relative;
padding: 0 @basePadding;
white-space: nowrap;
overflow-x: scroll;
position: relative;
// 兼容火狐
scrollbar-width: none;
// 兼容IE10+
-ms-overflow-style: none;
// 隐藏滚动条,兼容 chrome 和 safari 浏览器
&::-webkit-scrollbar {
display: none;
}
.tab-item {
scroll-margin: 30px;
display: inline-block;
flex: auto;
padding: 0 @basePadding / 2;
text-align: center;
white-space: nowrap;
font-size: 14px;
color: #666;
font-weight: 400;
}
.tab-item-active {
font-size: 16px;
font-weight: 600;
color: rgba(249, 43, 24, 1);
}
.tab-line {
height: 2px;
background-color: rgba(249, 43, 24, 1);
position: absolute;
bottom: 3px;
left: 0;
transition: left 0.3s;
content: '';
}
}
}
}
}
进阶tab组件,使下面横线固定宽度,不跟随tab标题宽度,将 .tab-line元素样式进行修改。
.tab-line {
height: 2px;
background-color: transparent;
position: absolute;
bottom: 3px;
left: 0;
transition: left 0.3s;
&::after {
background-color: rgba(249, 43, 24, 1);
width: 10px;
height: 2px;
display: block;
margin: auto;
content: '';
}
}