这是我参与「第四届青训营 」笔记创作活动的第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.
前面咱们写了oldValue
和activeIndex
做比较,假如不同就会翻滚目录元素.
幻想一下,假如咱们翻滚目录之后点击跳转,此刻咱们必定能够看到自己点击的内容高亮了,可是此刻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翻滚到顶就行了.
最后看看作用吧,特意找了个做动图的工具展现作用