需求:当前页面存在多个模块且内容很长时,需要提供一个锚点列表,可以快速查看对应模块内容
实现步骤:
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>
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.scrollTopstatus.anchors.forEach((v, index) => {let dom = document.getElementById(`${v.id}`)const offsetTop = dom.offsetTop const offsetHeight = dom.offsetHeightif (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) {status.anchorActive = index}})
}// 锚点跳转
const onAnchor = (item, index) => {// 点击锚点时,可以先移除scroll的监听事件等锚点动效结束后重新监听,不然会出现锚点高亮乱闪的问题scrollDom.value.removeEventListener('scroll', handleScroll)status.anchorActive = indexnextTick(() => {const target = document.getElementById(item.id); target?.scrollIntoView({ behavior: 'smooth',block: 'start', // 垂直对齐方式(start/center/end)inline: 'nearest' // 水平对齐方式});setTimeout(() => {scrollDom.value.addEventListener('scroll', handleScroll)}, 1000)})
}
</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;}}}}
最终效果: