一. 需求:左边是内容板块,右边是目录结构,点击右边内容跳转到左边相应位置展示,滑动左边内容右边目录自动跳转。
二、实现:
- 左边每一个内容模块都给一个ref和应该相同的class类名,方便获取dom;
- 左边内容区域使用滑动事件@scroll="handleScroll",内容区域滑动即触发该方法;
- 右边使用点击事件@click="goAnchor('anchor-' + index, index)"获取当前点击的dom;
- handleScroll() 滚动监听方法实现滑动左边内容对应上右边目录
- goAnchor() 右边锚点选中跳转相应内容区域,通过scrollIntoView({ behavior: 'smooth' })平滑跳转
3.代码:
<template>
<div class="panel-block flex">
<div class="panel-left" ref="anchorRef" @scroll="handleScroll">
<div class="panel-item anchor-item" ref="anchor-0">
<div class="panel-item-title">内容区域1</div>
xxx
</div>
<div class="panel-item anchor-item" ref="anchor-1">
<div class="panel-item-title">内容区域2</div>
xxx
</div>
<div class="panel-item anchor-item" ref="anchor-2">
<div class="panel-item-title">内容区域3</div>
<div class="panel-item-content">
xxx
</div>
</div>
<div class="panel-item anchor-item" ref="anchor-3">
<div class="panel-item-title">内容区域4</div>
<div class="panel-item-content">
xxx
</div>
</div>
</div>
<div class="panel-right">
<div class="step-line"></div>
<ul class="step-ul">
<li
class="flex_align_item_center"
v-for="(item, index) in stepData"
:key="index"
:class="{ 'step-active': activeBtn == index }"
@click="goAnchor('anchor-' + index, index)"
>
<div class="step-icon"></div>
<div class="step-label">{{ item }}</div>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
activeBtn: 0, //锚点按钮
stepData: ['区域1', '区域2', '区域3', '区域4'],
contentDiv: null, //锚点区域
}
},
methods: {
//锚点跳转
goAnchor(selector, index) {
this.activeBtn = index
this.$refs[selector].scrollIntoView({ behavior: 'smooth' })
},
// 滚动监听器
handleScroll(i) {
// 获取所有锚点元素
const navContents = document.querySelectorAll('.anchor-item')
// 所有锚点元素的 offsetTop
const offsetTopArr = []
navContents.forEach((item) => {
offsetTopArr.push(item.offsetTop)
})
// 获取当前文档流的 scrollTop
const scrollTop = this.$refs.anchorRef.scrollTop
offsetTopArr.forEach((item, index) => {
if (scrollTop >= item) {
this.activeBtn = index
}
})
},
}
}
</script>
-----------------以下为样式代码----------------------
<style lang="scss" scoped>
.panel-block {
height: 100%;
.panel-left {
width: calc(100% - 180px);
overflow-y: auto;
height: 100%;
.panel-item {
.panel-item-content {
padding: 10px;
border-top: 0;
min-height: 40px;
.table-params {
.table-header {
height: 40px;
line-height: 40px;
width: 100%;
border: 1px solid #ebeef5;
border-bottom: none;
background: #f5f7fa;
font-weight: 700;
font-size: 14px;
color: #303133;
padding-left: 15px;
}
.table-body-type {
height: 32px;
line-height: 32px;
width: 100%;
border: 1px solid #ebeef5;
border-bottom: none;
color: #303133;
padding-left: 15px;
.type-title {
font-weight: 700;
font-size: 12px;
}
}
}
}
}
}
.panel-right {
padding-top: 10px;
position: fixed;
left: calc(100% - 210px);
.step-line {
position: absolute;
left: 6px;
top: 0;
width: 1px;
background: #dcdfe6;
height: calc(50vh - 85px);
z-index: 0;
}
.step-ul {
li {
padding-bottom: 20px;
.step-icon {
width: 13px;
height: 13px;
margin-right: 20px;
border-radius: 50%;
z-index: 1;
}
.step-label {
font-weight: 700;
font-size: 14px;
line-height: 22px;
color: #303133;
}
}
.step-active {
.step-icon {
border: 1px solid #1989fe;
background: #fff;
}
.step-label {
color: #1989fe;
}
}
}
}
::-webkit-scrollbar {
width: 0 !important;
}
}
</style>