• 核心思路:根据index,渲染菜单选中样式,以及content的scroll定位。
  • 两步实现:
    1. 点击菜单时,通过scrollIntoView api 实现页面元素的平滑滚动。
    2. 注册滚动事件,反向同步菜单选中样式

代码实现如下(仅供参考):

<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 }}&nbsp;模块</div>
            <div class="content">{{ item }}&nbsp;内容</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>

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注