滚动导航组件实现
1.支持点击对应的索引,页面自动滚动到对应内容区域。
2.支持鼠标滚轮滚动左侧内容区域后,对应索引变为激活状态。
一.首先明白dom各高度几个概念
网页(元素)可见区域高:document.body.clientHeight
网页(元素)正文全文高:document.body.scrollHeight
网页(元素)被卷去的高:document.body.scrollTop
网页(元素)可见区域高(包括padding、border、水平滚动条):document.body.offsetHeight
屏幕分辨率高:window.screen.height
offsetTop应该是当前元素顶部距离最近的"设置了position属性的"父元素的距离,如果没有设置的话就是针对于body顶部的距离了
二、获取元素位置的方法
getPositions() {
let last = 0;
console.log("this.usedList",this.usedList)
//[{target: "#Step1",text: "基础管理工作哦"},{target: "#Step2",text: "医保管理工作"}]
this.positions = this.usedList.map((n, index) => {
let dom = document.querySelector(n.target);
let style = document.defaultView.getComputedStyle(dom);
let marginTop = parseInt(style["marginTop"].replace("px", ""), 10);
let marginBottom = parseInt(
style["marginBottom"].replace("px", ""),
10
);
//注意:是累加哦,offsetHeight就是高度 + padding + border
last += dom.offsetHeight + marginTop + marginBottom;
return last;
});
console.log("this.positions",this.positions)
//[260,435] 获取位置
this.areapositions = this.positions.map((n, index) => {
let r = [];
if (index === 0) {
r[0] = 0;
r[1] = n;
} else {
r[0] = this.positions[index - 1];
r[1] = n;
}
return r;
});
console.log("this.areapositions",this.areapositions)
// [[0, 260],[260, 435]] 获取位置
}
三、滚动监听并设置激活的索引
wheel(e) {
//先获取位置
this.getPositions();
//元素被卷去的高
let scrollTop = this.containerDom.scrollTop;
//网页可见区域高
let clientHeight = this.containerDom.clientHeight;
//网页正文全文高
let scrollHeight = this.containerDom.scrollHeight;
//如果被卷去高度为0,说明当前第一个元素激活
if (scrollTop === 0) {
this.nowIndex = 0;
}
//如果被卷去的高度 = 总高度 - 可见区域的高,说明当前为最后一个元素
else if (scrollTop === scrollHeight - clientHeight) {
this.nowIndex = this.positions.length - 1;
}
//
else {
_.forEach(this.areapositions, (item, index) => {
if (scrollTop >= item[0] && scrollTop <= item[1]) {
this.nowIndex = index;
return false;
}
});
}
}
四、点击索引滚动到指定位置
onChange(item, index) {
this.getPositions();
const go = () => {
this.nowIndex = index;
let toElement = document.querySelector(this.list[index].target);
toElement.scrollIntoView({
behavior: "smooth"
});
};
let flag = true;
//不考虑 clickable
if (item.clickable) {
flag = item.clickable(item, index);
}
if (flag) {
go();
}
}
五、完整代码
<template>
<div class="anchorNavWarpper">
<el-scrollbar>
<div
v-for="(item, index) in list"
:key="index"
class="navItem"
:class="classable(item, index)"
@click="onChange(item, index)"
>
<el-row class="kpiRow">
<el-col :span="8">
<el-tooltip v-if="item.text.length > 6" class="item" effect="dark" :content="item.text" placement="top">
<span>{{item.text.substr(0,5)}}...</span>
</el-tooltip>
<span v-else>{{item.text}}</span>
</el-col>
<el-col :span="10">{{ item.length }}</el-col>
<el-col :span="6">{{ item.sco }}</el-col>
</el-row>
</div>
</el-scrollbar>
</div>
</template>
<script>
import _ from "lodash";
export default {
name: "anchorTable",
props: {
list: {
type: Array,
default: () => {
return [];
}
},
//滚动区域的容器id,在其内部包含一个 el-scrollBar。后面拼.el-scrollbar__wrap 来获取滚动容器
container: {
type: String,
default: "#wnAppWarpper"
}
},
data() {
return {
nowIndex: 0,
containerDom: null,
dewheel: null,
positions: [],
areapositions: []
};
},
methods: {
//忽略此方法
setNowIndexByTarget(target) {
let find;
let index;
for (let i = 0; i < this.list.length; i++) {
let item = this.list[i];
if (item.target === target) {
find = item;
index = i;
break;
}
}
this.onChange(find, index);
},
//点击的时候让其滚动到指定位置
onChange(item, index) {
this.getPositions();
const go = () => {
this.nowIndex = index;
let toElement = document.querySelector(this.list[index].target);
toElement.scrollIntoView({
behavior: "smooth"
});
};
let flag = true;
//不考虑 clickable
if (item.clickable) {
flag = item.clickable(item, index);
}
if (flag) {
go();
}
},
//设定激活的类
classable(item, index) {
let clazz = index === this.nowIndex ? "navItem now" : "navItem";
if (item.clazz) {
clazz += " ";
clazz += item.clazz(item, index);
}
if (item.clickable && !item.clickable(item, index)) {
clazz += " old";
}
return clazz;
},
//获取各元素位置
getPositions() {
let last = 0;
console.log("this.usedList",this.usedList)
//[{target: "#Step1",text: "基础管理工作哦"},{target: "#Step2",text: "医保管理工作"}]
this.positions = this.usedList.map((n, index) => {
let dom = document.querySelector(n.target);
let style = document.defaultView.getComputedStyle(dom);
let marginTop = parseInt(style["marginTop"].replace("px", ""), 10);
let marginBottom = parseInt(
style["marginBottom"].replace("px", ""),
10
);
//注意:是累加哦,offsetHeight就是高度 + padding + border
last += dom.offsetHeight + marginTop + marginBottom;
return last;
});
console.log("this.positions",this.positions)
//[260,435] 获取位置
this.areapositions = this.positions.map((n, index) => {
let r = [];
if (index === 0) {
r[0] = 0;
r[1] = n;
} else {
r[0] = this.positions[index - 1];
r[1] = n;
}
return r;
});
console.log("this.areapositions",this.areapositions)
// [[0, 260],[260, 435]] 获取位置
},
//滚动事件
wheel(e) {
//先获取位置
this.getPositions();
//元素被卷去的高
let scrollTop = this.containerDom.scrollTop;
//网页可见区域高
let clientHeight = this.containerDom.clientHeight;
//网页正文全文高
let scrollHeight = this.containerDom.scrollHeight;
//如果被卷去高度为0,说明当前第一个元素激活
if (scrollTop === 0) {
this.nowIndex = 0;
}
//如果被卷去的高度 = 总高度 - 可见区域的高,说明当前为最后一个元素
else if (scrollTop === scrollHeight - clientHeight) {
this.nowIndex = this.positions.length - 1;
}
//
else {
_.forEach(this.areapositions, (item, index) => {
if (scrollTop >= item[0] && scrollTop <= item[1]) {
this.nowIndex = index;
return false;
}
});
}
}
},
computed: {
//这个没有做clickable的处理,可忽略
usedList() {
return _.filter(this.list, (item, index) => {
return !item.clickable || item.clickable(item, index);
});
}
},
mounted() {
//使用loadsh自带的节流函数触发滚动事件函数
this.dewheel = _.debounce(this.wheel, 300);
this.containerDom = document.querySelector(
this.container + " .el-scrollbar__wrap"
);
//绑定事件
if (document.addEventListener) {
this.containerDom.addEventListener("DOMMouseScroll", this.dewheel, false);
this.containerDom.addEventListener("mousewheel", this.dewheel, false);
} else {
this.containerDom.onmousewheel = this.dewheel;
}
},
destroyed() {
//销毁事件
if (document.addEventListener) {
this.containerDom.removeEventListener(
"DOMMouseScroll",
this.dewheel,
false
);
this.containerDom.removeEventListener("mousewheel", this.dewheel, false);
} else {
this.containerDom.onmousewheel = null;
}
}
};
</script>
<style scoped lang="scss">
.kpiRow {
.el-col {
border-left: 1px solid transparent;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
border-right: 1px solid #CECECE;
height: 100%;
}
&:first-child {
height: 40px;
line-height: 40px;
margin: 0 5px;
text-align: center;
.el-col {
&:last-child {
border-right: 1px solid transparent;
}
}
}
&:last-child {
height: 40px;
line-height: 40px;
margin: 0 5px;
border-bottom: 1px solid #CECECE;
text-align: center;
.el-col {
&:last-child {
border-right: 1px solid transparent;
}
}
}
}
.anchorNavWarpper {
width: 100%;
height: 260px
}
.navItem {
margin-bottom: 16px;
min-width: 56px;
height: 20px;
font-size: 14px;
font-weight: 500;
line-height: 20px;
color: #606266;
position: relative;
cursor: pointer;
&:hover {
color: #1b65b9;
}
&.now {
color: #1b65b9;
}
&.old {
cursor: default;
color: #c0c4cc;
&:hover {
color: #c0c4cc;
}
}
}
.round {
width: 8px;
height: 8px;
border-radius: 10px;
border: 2px solid #1b65b9;
position: absolute;
left: -17px;
top: 5px;
}
</style>