react Hook组件实现导航吸顶,锚点定位和页面滑动切换导航状态

背景:渲染几十个表格,页面很长。表格的个数和内容是后端返回的,不固定。
前端要增加一个导航栏,使页面快速滑动到目标表格。

首先根据表格的名字渲染出导航栏中的每个菜单。

点击菜单定位表格的思路:给循环的每个表格加ref,点击菜单,匹配到对应的表格的dom,
使页面滑动指定的距离— dom.offsetTop, 即表格距离上部的距离

页面滑动切换选中色的思路:监听页面滑动的事件onscroll,获取页面滑动的距离,判断页面滑动的距离和表格距离视窗口上部的距离 之间的关系,设置导航的选中色

过程中解决的问题:
1,react获取循环组件的ref
2,锚点定位
3,随着页面的滑动,切换导航栏的选中色
4,吸顶效果

先来看第一个问题,获取循环组件的ref:
useRef可以传数组,不过ref要直接加在dom元素上,在子组件中则无效,所以我在组件外面包裹了一层div
这样在页面渲染时就把每个组件的dom放到useRef中了。因为多次执行,surveyTableRefList 里面的数据有重复的,后面用时去重一下。

  const surveyTableRefList = useRef([]);
  function getSurveyTableRef(dom, key) {
    if (dom) {
      surveyTableRefList.current.push({
        dom: dom,
        key: key
      });
    }
  }
  surveyTableRefList.current = removeRepeatObj(surveyTableRefList.current, 'key');


  {Object.keys(tableList).map((item) => (
              
getSurveyTableRef(dom, item)} key={item}>

第二个问题锚点定位:在点击菜单时根据菜单名字匹配到对应的表格dom,然后让外面有滚动条的页面滚动表格dom的offsetTop距离。这里注意是让最外面有滚动条的dom滚动,而不是让表格滚动。
只有本身有滚动条的元素设置scrollTop才会起作用。

  // 点击部件名字时 锚点定位到选择的表格
  const clickSearchItem = (partName) => {
    // 因为surveyTableRefList.current这个数组里有两遍dom元素,取第二遍的是正确的顺序
    const correctDomArr = surveyTableRefList.current.slice(
      surveyTableRefList.current.length / 2,
      surveyTableRefList.current.length
    );
    const anchorElement = correctDomArr.find((item) => item.key == partName).dom;
    // anchorElement.scrollIntoView({ block: 'start', behavior: 'instant' }); // 不用这个方法了,有bug
    // 只有本身有滚动条的元素设置scrollTop才会起作用,所以改变整个页面的scrollTop
    equipInfoWrapRef.current.scrollTop = anchorElement.offsetTop - 164;
  };

第三个问题页面滑动,切换导航栏的选中色,思路是在页面滚动到上一个表格的中间和这一个表格的中间时,显示这一个表格.。这里在页面滚动时循环表格,判断滚动的距离在哪一个表格上,就设置导航的颜色在哪一个面。

  // 页面滚动时切换导航的选中状态; offsetTop 元素距离上部的距离  offsetHeight本身的高度
  useEffect(() => {
    equipInfoWrapRef.current.onscroll = (e) => {
      const correctDomArr = surveyTableRefList.current.slice(
        surveyTableRefList.current.length / 2,
        surveyTableRefList.current.length
      );
      //根据页面滚动的距离切换导航的选中状态
      for (let i = 0; i < correctDomArr.length; i++) {
        if (i > 0) {
          if (
            correctDomArr[i - 1].dom.offsetTop - 164 + correctDomArr[i - 1].dom.offsetHeight / 2 < e.target.scrollTop &&
            e.target.scrollTop < correctDomArr[i].dom.offsetTop - 164 + correctDomArr[i].dom.offsetHeight / 2
          ) {
            mutate('activePart', i);
          }
        } else {
          if (
            correctDomArr[i].dom.offsetTop - 200 < e.target.scrollTop &&
            e.target.scrollTop < correctDomArr[i].dom.offsetTop - 164 + correctDomArr[i].dom.offsetHeight
          ) {
            mutate('activePart', i);
          }
        }
      }
    };
  }, []);

第四个问题,吸顶效果,设置组件的样式为position: sticky;粘性定位,设置top:0,距离上部的距离,z-index:2,加大层级

.quickScreenWrap {
  padding-bottom: 10px;
  position: -webkit-sticky;
  position: sticky;
  top: 44px; // 因为上面有Tas组件
  z-index: 2;
  background: #fff;
}

你可能感兴趣的:(react Hook组件实现导航吸顶,锚点定位和页面滑动切换导航状态)