使用场景:当页面被分割成许多小模块,且页面很长时,锚点功能可以帮助我们快速跳转到想要的模块;而当滑动滚动条时,根据当前视图中的显示的模块自动选中对应的锚点。
思路
锚点的id对应模块的元素的id。点击锚点时,根据id找到对应的模块,获取此模块距离父级元素的顶部的距离,然后控制父级的滚动条scrollTo到此距离。
到此只实现了一半,即点击锚点页面滚动到对应的模块内。还需要实现锚点跟随滚动条的位置自动选中。
实现另一半需要监听滚动事件,在滚动事件中遍历锚点列表,根据锚点id获取对应的模块元素,计算当前滚动条位置处于哪个模块的位置内,从而将此锚点设为选中状态。
代码
html
<template><div class="info-body"><!--锚点列表--><ul class="archor"><li :class="activeStep === anchor.id ? 'active' : ''"v-for="(anchor, index) in anchorList":key="index" @click="jump(anchor.id)"><span>{{ anchor.name }}</span></li></ul><!--隐藏滚动条--><div class="hid-scroll-bar"><div class="scroll-box" id="scroll-box"><!--模块列表--><div v-for="(item, index) in modules"class="info-panel":key="index":id="item.id"><div class="info-content"><!--模块内容--></div></div></div></div></div>
</template>
js
data() {return {activeStep: '',// 默认选中的锚点的key值offsetTop: 0,}},computed: {scrollFn() { // 防抖return _.debounce(this.scroll, 100)},anchorList() { // 锚点列表return [{ id: 'anchor1', name: '锚点1' },{ id: 'anchor2', name: '锚点2' },{ id: 'anchor3', name: '锚点3' },];},modules() { // 模块列表return [{ id: 'anchor1', title: '模块1' },{ id: 'anchor2', title: '模块2' },{ id: 'anchor3', title: '模块3' },];},},mounted() {window.addEventListener('scroll', this.scrollFn, true)},beforeDestroy() {window.removeEventListener('scroll', this.scrollFn, false)},methods: {scroll() {const box = document.getElementById('scroll-box')// 若当前设置的滚动高度大于实际滚动的高度,即为锚点跳转,不再设置选中的锚点if(this.offsetTop > box.scrollTop) {this.offsetTop = 0return}let totalH = 0this.anchorList.some(anchor=> {let scrollItem = document.getElementById(anchor.id)// 锚点对应的模块totalH = totalH + scrollItem.clientHeightlet judge = box.scrollTop < totalHif(judge) {this.activeStep = anchor.idreturn true}})},jump(id) {this.activeStep = id // 设置选中的锚点为当前点击的const box = document.getElementById('scroll-box')let scrollItem = document.getElementById(id)// 锚点对应的模块与最近的一个具有定位的祖宗元素顶部的距离this.offsetTop = scrollItem.offsetTopbox.scrollTo({top: scrollItem.offsetTop,behavior: "smooth",});},}
css
/*锚点*/.archor {position: absolute;right: 16px;display: flex;flex-direction: column;width: 40px;list-style: none;li {height: 80px;border-right: 3px solid #FAFAFA;display: flex;flex-direction: column;justify-content: center;cursor: pointer;&:first-child {justify-content: flex-start;}&:last-child {justify-content: flex-end;}&.active {border-color: $primary-color;}}}.info-body {position: relative;width: 65%;border-left: 2px solid #F2F2F2;padding: 2px 82px 0 0;box-sizing: border-box;}/*双重包裹,隐藏滚动条*/.hid-scroll-bar {position: relative;width: 100%;height: 100%;.scroll-box {height: 100%;width: 100%;overflow-y: auto;position: absolute;&::-webkit-scrollbar {display: none;}}}
效果
相关连接
关于offsetTop的理解