- 核心思路:根据index,渲染菜单选中样式,以及content的scroll定位。
- 两步实现:
- 点击菜单时,通过
scrollIntoView
api 实现页面元素的平滑滚动。 - 注册滚动事件,反向同步菜单选中样式
- 点击菜单时,通过
代码实现如下(仅供参考):
<script setup>
import { reactive, ref} from "vue";
const blocks = ref([]);
const state = reactive({
menus:['菜单1','菜单2','菜单3','菜单4','菜单5',],
current: 0,
blocks:[],
});
//=================== Methods ====================
/**
* 菜单点击事件
* */
const handleClick = idx => {
state.current = idx;
// 目标滚动到可视区域
document.querySelectorAll('.block')[idx].scrollIntoView({ block: 'start', behavior: "smooth"})
};
/**
* 获取锚点数组
* */
const setBlocks = (block) => {
blocks.value.push(block);
};
/**
* 滚动事件
* */
const handleScroll = (e) => {
const block = document.querySelectorAll('.block');
// 从后往前判断
for (let i = block.length-1; i >= 0 ; i--) {
const scrollView = e.target.scrollTop >= block[i].offsetTop;
if(scrollView){
state.current = i;
break;
}
}
};
</script>
<template>
<div class="container">
<div class="header">
<h3>scrollInTo示例</h3>
</div>
<div class="left">
<ul>
<li
:class="{'selected' : state.current === index }"
v-for="(item,index) in state.menus"
@click="handleClick(index)"
>{{item}}</li>
</ul>
</div>
<div class="right" @scroll="handleScroll">
<div class="block"
:ref="setBlocks"
v-for="(item, index) in state.menus"
>
<div class="title">{{ item }} 模块</div>
<div class="content">{{ item }} 内容</div>
</div>
</div>
</div>
</template>
<style scoped lang="less">
@select-color: #F73802;
ul,li{
margin: 0;
padding: 0;
list-style: none;
}
.container{
width: 100%;
height: 100%;
background: #C9D1DB40;
padding: 20px;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-items: flex-start;
align-content: flex-start;
.header{
height: 60px;
width: 100%;
background: white;
box-shadow: 0 10px 10px #88888860;
margin-bottom: 20px;
padding: 5px 20px;
display: flex;
align-items: center;
border-radius: 8px;
}
.left{
width: 200px;
background: white;
border-radius: 8px;
box-shadow: 0 10px 10px #88888860;
padding: 20px;
position: sticky;
ul{
li{
color: #888888;
position: relative;
cursor: pointer;
&:not(:last-child){
margin-bottom: 10px;
}
&:hover{
color: @select-color;
font-weight: bold;
}
&.selected{
color: @select-color;
font-weight: bold;
&:before{
content: "";
width: 4px;
height: 4px;
border-radius: 50%;
position: absolute;
left: -8px;
top: 50%;
transform: translateY(-50%);
background: @select-color;
}
}
}
}
}
.right{
width: 350px;
height: 400px;
overflow-x: hidden;
overflow-y: auto; // 实现滚动
box-shadow: 0 10px 10px #88888860;
position: relative; // 用于子元素获取offsetTop
.block{
width: 100%;
border-radius: 8px;
background: white;
margin-bottom: 20px;
box-shadow: 0 10px 10px #88888860;
.title{
height: 30px;
border-bottom: 1px solid #c0c0c060;
display: flex;
align-items: center;
color: @select-color;
padding-left: 20px;
}
.content{
padding-left: 20px;
min-height: 150px;
}
}
}
}
</style>