滚动tab导航栏+吸顶效果: React + antd

2,795 阅读2分钟

需求

  1. tab栏点击切换,页面滚动至对应的部分
  2. 页面鼠标滚动至特定范围,相应的tab栏激活
  3. 距离顶部一定高度后,显示吸顶菜单;小于指定高度时,隐藏吸顶菜单
  4. tab导航栏和吸顶菜单状态同步。

tab.gif

代码

导入所需的库

import React, { useState, useEffect } from "react";
import { Tabs } from "antd";
import { menu } from "./datamap"; // tab数组
const { TabPane } = Tabs;

用到的常量/state

const [activeSecMenu, setActiveSecMenu] = useState("1"); // 激活菜单的key
// 是否激活吸顶
const [fixedFlag, setFixedFlag] = useState(false);

// 滚动
const [secTop, setSecTop] = useState([]);
const [secTopList, setSecTopList] = useState([]);
const [isScroll, setScroll] = useState(false)

temp方法

// 菜单的所有tab对应的距离顶部的高度数组
const getSecMenuTop = () => {
    let secDomList = [];
    let secTop = [];
    menu.map((item) => {
      let id = item.key;
      if (id) {
        let dom = document.getElementById(id);
        if (dom) {
          let top = getDomToTop(dom) - 160;
          let secMenuItem = {
            id: item.key,
            top: top,
          };
          secTop.push(top);
          secDomList.push(secMenuItem);
        }
      }
    });
    // console.log("secdom", secDomList);
    // console.log("sectop", secTop);
    setSecTopList(secDomList);
    setSecTop(secTop);
};
// 获取元素到顶部的距离
  const getDomToTop = (el) => {
    if (el.parentElement) {
      return getDomToTop(el.parentElement) + el.offsetTop;
    }
    return el.offsetTop;
  };

html结构

<div className="ceilingtab">
      <Tabs activeKey={activeSecMenu} onChange={handleSecMenu} id="SecMenu">
        {menu.length > 0 &&
          menu.map((item) => {
            return <TabPane tab={item.name} key={item.key}></TabPane>;
          })}
      </Tabs>
      {menu.map((item) => {
        return (
          <div
            className="menu-item"
            id={item.key}
            style={{ backgroundColor: item.color }}
          >
            {item.name}
          </div>
        );
      })}

      <div className={fixedFlag ? "show-fixed-menu" : "hidden-fixed-menu"}>
        <Tabs activeKey={activeSecMenu} onChange={handleSecMenu}>
          {menu.length &&
            menu.map((k) => <TabPane tab={k.name} key={k.key}></TabPane>)}
        </Tabs>
      </div>
    </div>

处理函数

监听鼠标事件

// 监听鼠标滚动
  const handleScrollFunc = (event) => {
    // 当前滚动的高度
    const scrollTop =
      (event.srcElement ? event.srcElement.scrollTop : false) ||
      window.pageYOffset ||
      (event.srcElement ? event.srcElement.body.scrollTop : 0);
    const secMenuDom = document.getElementById('SecMenu');
    // 最外层导航元素至顶部的距离
    const secMenuTop = secMenuDom && secMenuDom.offsetTop;
    // 如果导航元素存在,并且页面滚动了
    if (scrollTop && secMenuTop) {
      // 页面滚动的高度>距顶高度,说明tab栏滚走了,吸顶栏需要显示
      if (scrollTop >= secMenuTop) {
        setFixedFlag(true);
      } else {
        setFixedFlag(false);
      }
    }
    // 根据滚动的高度,判断应该激活的菜单    
    scrollTop && handleScrollActiveSecMenu(scrollTop);
  };

根据当前滚动d的高度判断tab菜单的选中

  const handleCurrentSecMenu = (index) => {
    let activeSecMenu = '';
    secTopList[index] && secTopList[index].id ? (activeSecMenu = secTopList[index].id) : '';
    if (activeSecMenu) {
      setActiveSecMenu(activeSecMenu);
    }
  };

点击/激活tab菜单

const handleScrollActiveSecMenu = (scrollTop) => {
    let currentIndex = 0;
    secTop.map((item, index) => {
      let top = item;
      if (scrollTop >= top) {
        currentIndex = index;
      }
    });
    
    handleCurrentSecMenu(currentIndex);
  };

点击tab后的滑动

const scrollToAnchor = (anchorId) => {
    // 找到锚点 id
    if (anchorId) {
      let anchorElement = document.getElementById(anchorId);
      // console.log(anchorId, 'anchorId')
      // 如果对应id的锚点存在,就跳转到锚点
      if (anchorElement) {
        // getDomToTop算出来的高度总比实际高度大一点
        let top = getDomToTop(anchorElement) - 60;
        window.scrollTo({
          top: top,
          behavior: 'smooth',
        });
        // setTimeout(() => {
        //   setScroll(false);
        // }, 400);
      }
    }
    setScroll(false)
  };

完整代码在github:github.com/gmx-white/s…

点个赞再走哇~