这是我参与「第四届青训营 」笔记创作活动的第6天

仿项目之完成文章预览|青训营笔记 – ()

仿项目之文章目录完成|青训营笔记 – ()

仿项目之文章目录翻滚高亮完成 | 青训营笔记 – ()

书接上文

前面我现已完成了根本的目录翻滚高亮作用,可是还有个大bug没有处理,这个bug便是当目录内容过多,部分目录要经过翻滚条的方式才干显现.也便是说有一部分目录会被隐藏起来.

那么当文章翻滚到对应的目录隐藏部分时,目录会跟着文章翻滚让当时高亮内容显现在可视区域吗?

答案是不会的,所以滚着滚着,高亮元素就看不到了.

所以咱们有必要自己完成这个功用

获取目录元素

<ul ref="nav" class="nav">
      <li v-for="(i, index) in list" :key="index" :title="i.content" @click="jump(index)" :ref="setItemRef"
        :class="activeIndex === index ? 'active' : ''">
        <div :style="{ marginLeft: size(i.id) }">
          {{ i.content }}
        </div>
      </li>
    </ul>

这儿经过ref='nav'获取目录元素,经过:ref="setItemRef"这个能够获取到所以li元素,也便是每一条目录所在元素

const nav = ref(null);
//获取nav中的li元素
let itemRefs = [];
const setItemRef = (el) => {
  if (el) {
    itemRefs.push(el);
  }
};
onBeforeUpdate(() => {
  itemRefs = [];
});

仿掘金项目之文章长目录滚动高亮优化 | 青训营笔记

判别当时高亮目录li元素是否超越目录元素总高度的一半

这儿是因为观察的长目录,当高亮元素翻滚到总高度一半时,才让nav元素翻滚起来.

    let mid = nav.value.clientHeight / 2;  //翻滚元素父元素的高度的一半
    let offsetTop = itemRefs[activeIndex.value].offsetTop; //当时激活元素相对于父元素顶部的间隔
     if (offsetTop > mid ) {
        nav.value.scrollBy(0, 32);
      }

scrollBy(x,y)接收两个参数,横坐标与纵坐标,是相对于当时方位翻滚指定间隔

留意这儿是让nav元素翻滚,不是widow或者document,并且留意nav得有翻滚条才有效

这儿的32是我一个li元素的高度

判别当时高亮元素与上一个高亮元素的差值

这一点是因为,假如咱们翻滚的飞快,那么目录翻滚的速度会跟不上,比如咱们一次翻滚跨越了三四个h标签,可是目录翻滚只移动了一位,这样积累之下,当时的高亮元素就会消失在可视区域内.

  if (oldValue === activeIndex.value) {
    return;
  }
  let difference = activeIndex.value - oldValue //差值
  if (offsetTop > mid ) {
    nav.value.scrollBy(0, 32 * difference);
  }
  oldValue = activeIndex.value;

在大局中界说遍历oldValue = 0

假如当时activeIndex等于oldValue,就说明当时高亮元素没改动,不需要移动

经过difference能够知道当时activeIndex和上一个activeIndex相差多少,移动对应的间隔,让高亮元素尽量在目录的中心方位.

写文章的时候现场发现一个bug,当文章翻滚到最后,再向上翻滚时,目录也会跟着移动,这样的话,高亮元素一直在最下面,这样不好看,所以我又改了改

  if (oldValue === activeIndex.value) {
    return;
  }
  let difference = activeIndex.value - oldValue //差值
   if (offsetTop > itemRefs[itemRefs.length - 1].offsetTop - mid && !isDown) {
  }
  else if (offsetTop > mid ) {
    nav.value.scrollBy(0, 31 * difference);
  }
  oldValue = activeIndex.value;

当文章在最下面的一部分,并且是向上翻滚时,nav不跟着翻滚,这个和在最上面没超越nav的一半不翻滚是一个道理

这儿的isDown表明的是文章是向上翻滚仍是向下翻滚

let isDown = true;
//判别翻滚方向
let scrollFunc = function (e) {
  e = e || window.event;
  if (e.wheelDelta) {
    //判别浏览器IE,谷歌滑轮事情
    if (e.wheelDelta > 0) {
      //当滑轮向上翻滚时
      // console.log("滑轮向上翻滚");
      isDown = false;
    }
    if (e.wheelDelta < 0) {
      //当滑轮向下翻滚时
      // console.log("滑轮向下翻滚");
      isDown = true;
    }
  } else if (e.detail) {
    //Firefox滑轮事情
    if (e.detail > 0) {
      //当滑轮向上翻滚时
      isDown = false;
      // console.log("滑轮向上翻滚");
    }
    if (e.detail < 0) {
      //当滑轮向下翻滚时
      isDown = false;
      // console.log("滑轮向下翻滚");
    }
  }
};
const mouseWheel = () => {
  if (document.addEventListener) {
    //火狐运用DOMMouseScroll绑定
    document.addEventListener("DOMMouseScroll", scrollFunc, false);
  }
  //其他浏览器直接绑定翻滚事情
  document.addEventListener("mousewheel", scrollFunc);
};

到这儿长目录跟从翻滚的功用根本完成了.

仿掘金项目之文章长目录滚动高亮优化 | 青训营笔记

处理点击跳转带来的bug

假如咱们不点击跳转,单纯的跟从翻滚高亮现已没问题了,可是假如点击跳转会如何?

看过前面内容的应该知道,点击之后高亮点击的目录内容,一起将当时的index值赋值给activeIndex.

前面咱们写了oldValueactiveIndex做比较,假如不同就会翻滚目录元素.

幻想一下,假如咱们翻滚目录之后点击跳转,此刻咱们必定能够看到自己点击的内容高亮了,可是此刻activeIndex现已改动,并且因为咱们翻滚了nav目录,那么这两个值的差就会很大,造成的影响便是高亮内容直接被翻滚到非可视区域了,咱们看不见了.

这必定不是咱们想要的作用.

我用了一个比较简单的方法处理这个问题

//点击目录跳转
let isJump = false
const jump = (index) => {
  activeIndex.value = index;
  let target = document.getElementById(index).offsetTop;
  if (target) {
    window.scrollTo({
      top: target - 80,
    });
    isJump = true
  }
};

这是之前写的点击跳转的代码

我在这儿加了个flag:isJamp,当我点击之后就设置成true

const watchActive = () => {
  if (oldValue === activeIndex.value) {
    return;
  }
  let difference = activeIndex.value - oldValue //差值
  let mid = nav.value.clientHeight / 2;  //翻滚元素父元素的高度的一半
  let offsetTop = itemRefs[activeIndex.value].offsetTop; //当时激活元素相对于父元素顶部的间隔
  oldValue = activeIndex.value;
  if (isJump) {
    isJump = false
    return
  }
  if (offsetTop > itemRefs[itemRefs.length - 1].offsetTop - mid && !isDown) {
  }
  else if (offsetTop > mid ) {
    nav.value.scrollBy(0, 31 * difference);
  }
  if (activeIndex.value === 0) {//后边解说这个
    nav.value.scrollTo({
      top: 0
    })
  }
};

这儿是前面写的代码的整体内容,在scroll事情的回调函数中调用就行了.

这儿我经过isJump的值来判别是不是点击跳转导致的activeIndex改变,假如是,后边翻滚nav的代码就不会履行.这样就处理了bug.

可是不行完美,假如我点击的是偏下的目录,那么高亮的元素就一直在偏下的方位,不会在翻滚中移动到中心方位

这儿有个想法,仍是依据高亮元素的方位改动移动间隔的大小,让其一直在中心方位. 偷闲不写了.感觉有点麻烦

处理点击回到顶部带来的bug

前面提到点击回到顶部的功用也是我写的,所以我在测验时发现,假如我点击回到顶部之后,nav目录可视区域仍是在点击回到顶部之前所对应的区域,也便是我看不到当时的高亮元素.

仿掘金项目之文章长目录滚动高亮优化 | 青训营笔记
这个bug是怎样造成的呢.

 if (offsetTop > mid ) {
    nav.value.scrollBy(0, 31 * difference);
  }

只要翻滚元素所在方位大于nav总高度的一半才干履行翻滚的代码.

点击回到顶部时,activeIndex = 0,对应的是第一个li标签,这必定小于nav的高度的一半,所以这个翻滚就不会履行

处理办法便是我前面写的

 if (activeIndex.value === 0) {
    nav.value.scrollTo({
      top: 0
    })
  }

回到顶部之后,activeIndex = 0,这个时候履行一下scrollTo,将nav翻滚到顶就行了.

最后看看作用吧,特意找了个做动图的工具展现作用

仿掘金项目之文章长目录滚动高亮优化 | 青训营笔记