手写tabs是组件-react

100 阅读2分钟

手写tabs组件,功能包括:

  1. 功能包括点击tab自动回到可视区
  2. 底部横线滑动效果
  3. 下面的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: '';
                    }
                }