Tab点击滚动效果

659 阅读1分钟

今天来实现下抖音顶部Tab栏点击时自动滚动到中间的效果。掘友们有更好的实现方法欢迎指教~~

前置知识

getBoundingClientRect

Element.getBoundingClientRect() 方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口(可以理解为可见区域,不包含滚动导致的不可见区域)的位置。

image.png

如果你需要获得边界矩形相对于整个网页左上角的位置,则可以将当前的滚动位置(可通过 window.scrollXwindow.scrollY 获得)添加到 topleft 属性上。获得的边界矩形与当前的滚动位置无关。

scrollLeft

Element.scrollLeft 属性可以读取或设置元素滚动条到元素左边的距离。

注意如果这个元素的内容排列方向(direction)是rtl (right-to-left) ,那么滚动条会位于最右侧(内容开始处),并且scrollLeft值为 0。此时,当你从右到左拖动滚动条时,scrollLeft 会从 0 变为负数。

代码实现

// app.tsx
import { useState } from 'react';
import './App.css';

function App() {
  const initialTabs = [];
  const [activeKey, setActiveKey] = useState('1');

  for (let i = 0; i < 10; i++) {
    initialTabs.push({
      name: `Tab${i + 1}`,
      key: `${i + 1}`,
    });
  }
  const [tabs] = useState(initialTabs);

  const onClickTab = (key: string) => {
    setActiveKey(key);
    const selector = `#tab${key}`;
    const element = document.querySelector(selector);
    const elementRect = element?.getBoundingClientRect();
    const container = document.querySelector('.tab-container');
    const containerRect = container?.getBoundingClientRect();

    const halfWidth = (containerRect as DOMRect).width / 2;
    console.log('halfWidth', halfWidth);
    console.log('container', containerRect);
    console.log('elementRect', elementRect);
    if ((elementRect as DOMRect).left > halfWidth) {
      // tab位于屏幕的右半部分,需要向左滚动
      const scrollValue =
        elementRect!.left - halfWidth + elementRect!.width / 2;
      container && (container.scrollLeft += scrollValue);
    } else {
      // tab位于屏幕的左半部分,需要向左滚动
      const scrollValue =
        halfWidth - elementRect!.left - elementRect!.width / 2;
      container && (container.scrollLeft -= scrollValue);
    }
  };

  return (
    <>
      <div className="tab-container">
        {tabs.map((tab) => (
          <div
            key={tab.key}
            className={`tab-item ${activeKey === tab.key && 'tab-item-active'}`}
            id={`tab${tab.key}`}
            onClick={() => onClickTab(tab.key)}
          >
            {tab.name}
          </div>
        ))}
      </div>
      <div className="main">页面区域</div>
    </>
  );
}

export default App;

/* app.css*/
* {
  box-sizing: border-box;
}
.tab-container {
  width: 375px;
  display: flex;
  max-width: 100vw;
  overflow-x: auto;
  padding: 10px 20px;
  /* 隐藏滚动条 Firefox*/
  scrollbar-width: none;
  /* 隐藏滚动条  IE 10+*/
  -ms-overflow-style: none;
}
/* Chrome、Safari 和 Opera 的滚动条  */
.tab-container::-webkit-scrollbar {
  display: none;
}

.tab-item {
  margin-right: 3rem;
  cursor: pointer;
}
.tab-item-active {
  color: red;
}

.tab-item:last-child {
  margin-right: 0;
}
.main {
  width: 375px;
  height: 20rem;
  background-color: bisque;
  margin-top: 1rem;
}