使用场景:当页面被分割成许多小模块,且页面很长时,锚点功能可以帮助我们快速跳转到想要的模块;而当滑动滚动条时,根据当前视图中的显示的模块自动选中对应的锚点。

思路

  锚点的id对应模块的元素的id。点击锚点时,根据id找到对应的模块,获取此模块距离父级元素的顶部的距离,然后控制父级的滚动条scrollTo到此距离。

  到此只实现了一半,即点击锚点页面滚动到对应的模块内。还需要实现锚点跟随滚动条的位置自动选中。

  实现另一半需要监听滚动事件,在滚动事件中遍历锚点列表,根据锚点id获取对应的模块元素,计算当前滚动条位置处于哪个模块的位置内,从而将此锚点设为选中状态。

代码

html

<template>
   <div class="info-body">
      <!--锚点列表-->
      <ul class="archor">
        <li :class="activeStep === anchor.id ? 'active' : ''"
            v-for="(anchor, index) in anchorList"
            :key="index" @click="jump(anchor.id)">
          <span>{{ anchor.name }}</span>
        </li>
      </ul>

      <!--隐藏滚动条-->
      <div class="hid-scroll-bar">
        <div class="scroll-box" id="scroll-box">

          <!--模块列表-->
          <div v-for="(item, index) in modules"
               class="info-panel"
               :key="index"
               :id="item.id">
            <div class="info-content">
              <!--模块内容-->
            </div>
          </div>

        </div>
      </div>

   </div>
</template>

js

data() {
    return {
      activeStep: '',// 默认选中的锚点的key值
      offsetTop: 0,
    }
  },
  computed: {
    scrollFn() { // 防抖
      return _.debounce(
        this.scroll, 100
      )
    },
    anchorList() { // 锚点列表
      return [
       { id: 'anchor1', name: '锚点1' },
       { id: 'anchor2', name: '锚点2' },
       { id: 'anchor3', name: '锚点3' },
      ];
    },
    modules() { // 模块列表
      return [
        { id: 'anchor1', title: '模块1' },
        { id: 'anchor2', title: '模块2' },
        { id: 'anchor3', title: '模块3' },
      ];
    },
  },
  mounted() {
    window.addEventListener('scroll', this.scrollFn, true)
  },
  beforeDestroy() {
    window.removeEventListener('scroll', this.scrollFn, false)
  },
  methods: {
    scroll() {
      const box = document.getElementById('scroll-box')
      // 若当前设置的滚动高度大于实际滚动的高度,即为锚点跳转,不再设置选中的锚点
      if(this.offsetTop > box.scrollTop) {
        this.offsetTop = 0
        return
      }
      let totalH = 0
      this.anchorList.some(anchor=> {
        let scrollItem = document.getElementById(anchor.id)// 锚点对应的模块
        totalH = totalH + scrollItem.clientHeight
        let judge = box.scrollTop < totalH
        if(judge) {
          this.activeStep = anchor.id
          return true
        }
      })
    },
    jump(id) {
      this.activeStep = id // 设置选中的锚点为当前点击的
      const box = document.getElementById('scroll-box')
      let scrollItem = document.getElementById(id)
      // 锚点对应的模块与最近的一个具有定位的祖宗元素顶部的距离
      this.offsetTop = scrollItem.offsetTop
      box.scrollTo({
        top: scrollItem.offsetTop,
        behavior: "smooth",
      });
    },

  }

css

 /*锚点*/
    .archor {
      position: absolute;
      right: 16px;
      display: flex;
      flex-direction: column;
      width: 40px;
      list-style: none;

      li {
        height: 80px;
        border-right: 3px solid #FAFAFA;
        display: flex;
        flex-direction: column;
        justify-content: center;
        cursor: pointer;

        &:first-child {
          justify-content: flex-start;
        }

        &:last-child {
          justify-content: flex-end;
        }

        &.active {
          border-color: $primary-color;
        }
      }

    }

    .info-body {
      position: relative;
      width: 65%;
      border-left: 2px solid #F2F2F2;
      padding: 2px 82px 0 0;
      box-sizing: border-box;
    }

    /*双重包裹,隐藏滚动条*/
    .hid-scroll-bar {
      position: relative;
      width: 100%;
      height: 100%;

      .scroll-box {
        height: 100%;
        width: 100%;
        overflow-y: auto;
        position: absolute;

        &::-webkit-scrollbar {
          display: none;
        }
      }
    }

效果

相关连接

关于offsetTop的理解

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐