需求:当前页面存在多个模块且内容很长时,需要提供一个锚点列表,可以快速查看对应模块内容
实现步骤:
1.每个模块添加唯一id,添加锚点列表div
<template>
<!-- 模块A -->
<div id="modalA">
<ModalA/>
</div>
<!-- 模块B -->
<div id="modalB">
<ModalB/>
</div>
<!-- 动态锚点 -->
<div class="anchor-box">
<ul>
<li v-for="(item, index) in status.anchors" :key="index" :class="{'active': status.anchorActive===index}" @click="onAnchor(item, index)">
<i class="spot"></i>
<span>{{ item.title }}</span>
</li>
</ul>
</div>
</template>
2.初始化锚点列表,监听滚动事件
<script lang='ts' setup>
import { debounce } from "lodash-es";
const status:any = reactive({
anchors: [], // 动态锚点集合
anchorActive: -1, // 锚点高亮
});
onMounted(() => {
init()
});
const init = () => {
initAnchors()
// 这里的content-wrap是设置overflow-y:auto的div,如果滚动条是全局的可以改成window,即:window.addEventListener('scroll', handleScroll)
scrollDom.value = document.querySelector('.content-wrap')
scrollDom.value.addEventListener('scroll', handleScroll)
}
// 设置动态锚点集合
const initAnchors = () => {
status.anchors = [
{
title: "模块A",
id: "modalA"
},
{
title: "模块B",
id: "modalB"
},
]
}
// 监听滚动
const handleScroll = () => {
let scrollPosition = scrollDom.value.scrollTop
status.anchors.forEach((v, index) => {
let dom = document.getElementById(`${v.id}`)
const offsetTop = dom.offsetTop
const offsetHeight = dom.offsetHeight
if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) {
status.anchorActive = index
}
})
}
// 锚点跳转
const onAnchor = (item, index) => {
// 移除scroll事件
scrollDom.value.removeEventListener('scroll', handleScroll)
status.anchorActive = index
nextTick(() => {
const target = document.getElementById(item.id);
target?.scrollIntoView({
behavior: 'smooth',
block: 'start', // 垂直对齐方式(start/center/end)
inline: 'nearest' // 水平对齐方式
});
// 重新绑定scroll事件
addScrollEvent()
})
}
// 绑定scroll事件
const addScrollEvent = debounce(() => {
scrollDom.value.addEventListener('scroll', handleScroll)
}, 1500)
</script>
3.设置锚点样式
.anchor-box {
position: fixed;
top: 100px;
right: 50px;
width: 120px;
height: calc(100vh - 80px - 40px);
overflow-y: auto;
&::-webkit-scrollbar {
width: 0;
}
ul {
position: relative;
list-style: none;
padding: 4px;
box-sizing: border-box;
background-color: rgba(240, 242, 245, 0.8);
&:before {
position: absolute;
top: 0;
left: 7px;
width: 2px;
height: 100%;
background-color: #dbdada;
content: " ";
}
li {
position: relative;
width: 100%;
display: flex;
margin-bottom: 6px;
cursor: pointer;
&.active {
.spot {
display: block;
}
> span {
color: #1890ff;
}
}
.spot {
display: none;
position: absolute;
top: 6px;
width: 8px;
height: 8px;
background-color: #fff;
border: 2px solid #1890ff;
border-radius: 50%;
transition: top .3sease-in-out;
}
> span {
width: 100%;
line-height: 1.5;
padding-left: 16px;
box-sizing: border-box;
font-size: 13px;
}
}
}
}
最终效果: