菜单跟随滚动内容进行滚动显示

原理为监听页面滚动事件,计算每个菜单容器clientHeight的高度和scollHeight高度,判断scollHeight是否已经大于容器位置的高度,若已达到了容器位置的高度则, 对应菜单则高亮显示。
直接上代码:

<div>
    <div class="container" ref="container">
    	<!--菜单栏位置-->
        <ul class="nav">
            <li v-for="i in 6" :key="i">
                <p
                        :class="{'active-font': i === activeNo}"
                        ref="nav"
                        @click="navClick(i)"
                >
                    菜单{{i}}
                </p>
            </li>
        </ul>
        <!--菜单对应正文位置-->
        <article>
            <div
                    :style="{
                        backgroundColor: getBackColor(i),
                        height: 100 * i + 'px'
                    }"
                    v-for="i in 6"
                    :key="i"
                    ref="article"
            >
                菜单{{i}}
            </div>
        </article>
    </div>
</div>

js

data() {
    return {
        activeNo: 1  //菜单标题高线显示判断, 默认菜单标题第一个高亮
        flag: null //判断点击了左边菜单栏的那个菜单
    }
},
methods: {
    // 防抖, 防止方法多次判断, 性能优化
    debounce(func, wait, immediate) {
        let timeout, args, context, timestamp, result;
        const later = function () {
            // 据上一次触发时间间隔
            const last = new Date() - timestamp;
            // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
            if (last < wait && last > 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };

        return function () {
            context = this;
            args = arguments;
            timestamp = new Date();
            const callNow = immediate && !timeout;
            // 如果延时不存在,重新设定延时
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }
            return result;
        };
    },
    //菜单标题点击事件
    navClick(i) {
    	this.flag = i;
    	//获取菜单内容显示位置
        const scrollBox1 = this.$refs.article[i - 1];
        //滚动到菜单内容位置
        scrollBox1.scrollIntoView();
        //对应菜单标题高亮显示
        this.activeNo = i
    },
    //内容背景色
    getBackColor(index) {
        const arr = ['', '#e6e6e6', '#fd5331', '#c00ab9', '#149ce1', '#f99831', '#0aff3c', '#dbe6f7'];
        return arr[index]
    }
},
mounted() {
    const self = this;
    const scrollCallback = self.debounce(() => {
    	//菜单内容容器集合
        const articleList = self.$refs.article;
        //每个article容器高度
        const articleHeight = articleList.map(item => {
            return item.clientHeight;
        });   
        
        let scrollTopList = []; //每个article距离顶部的高度
        for (let i = 0; i < articleHeight.length; i++) {
            if (i === 0) scrollTopList.push(articleHeight[i]);
            else{
                let num = 0;
                for (let j = 0; j <= i; j++) {
                    num += articleHeight[j]
                }
                scrollTopList.push(num)
            }
        }
        
        const top = self.$refs.container.scrollTop;  //设置变量top,表示当前滚动条到顶部的值
        /*
        *   特殊情况, 当滚动到底部且页面存在多个菜单, 导致菜单栏无法高亮显示
        *   设置flag, 判断是通过点击具体哪个菜单的跳转  flag为null表示滚动事件
        * */
        if (self.flag !== null){
            //当前点击的菜单距离顶部的偏移高度
            const curMeunOffTopHeight = scrollTopList[self.flag - 1];
            //若当前点击的菜单距离顶部的偏移高度大于滚动高度则不继续往下执行
            if (curMeunOffTopHeight >= top) {
                self.flag = null;
                return
            }
        }
        for (let i = 0; i < scrollTopList.length; i++) {
        	//判断滚动条滚动的高度对应的菜单, 使菜单标题高亮显示
            if (i === 0 && top < scrollTopList[i]) self.activeNo = i + 1;
            else {
                if (top >= scrollTopList[i - 1] && top < scrollTopList[i]) self.activeNo = i + 1;
            }
        }

    }, 100);
    //页面容器
    const container = self.$refs.container;
    //监听滚动事件
    container.addEventListener('scroll', scrollCallback);
	//页面销毁时,移除滚动事件监听,提高性能
    this.$on("hook:beforeDestroy", () => {
        container.removeEventListener('scroll', scrollCallback);
    })
}

css

	* {
        padding: 0;
    }

    li {
        list-style-type: none
    }

    .container {
        position: absolute;
        top: 0;
        right: 0;
        left: 0;
        display: flex;
        overflow: auto;
        width: 100%;
        height: 500px;
        margin: auto
    }

    .nav {
        position: fixed;
        z-index: 5;
        display: inline-flex;
        flex-wrap: wrap;
        width: 100px;
        height: inherit;
        background: #fff
    }

    .nav li {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%
    }

    article {
        display: inline-block;
        width: 80%;
        margin-left: 100px
    }

    article > div {
        width: 100%;
        display: flex;
        font-size: 25px;
        padding-left: 100px;
        align-items: center;
    }

    .active-font {
        border-bottom: 2px solid dodgerblue;
        font-weight: bold
    }

页面效果
在这里插入图片描述

本文参考与文章:https://www.jianshu.com/p/9a7ed6ff2928

`el-menu` 是 Element UI 提供的一个用于创建导航菜单组件,常用于单页面应用(Single Page Application, SPA)中,它可以根据路由数据动态生成菜单项。在 Vue.js 中结合 `vue-router` 使用,可以实现菜单跟随路由变化而更新显示内容。 基本用法如下: 1. 安装依赖: 首先确保已经安装了 Element UI 和 vue-router。在项目中运行 `npm install element-ui vue-router` 或者 `yarn add element-ui vue-router`。 2. 引入组件: 在 Vue 组件中导入 `ElMenu` 和 `ElMenuItem`: ```js import { ElMenu, ElMenuItem } from 'element-ui'; ``` 3. 创建路由: 在 `main.js` 或其他配置文件中配置路由: ```js import Router from 'vue-router'; import Home from '@/views/Home.vue'; // 示例路由 const routes = [ { path: '/', component: Home, children: [ // 更具体的路由配置... ] } ]; const router = new Router({ routes }); // 然后需要在Vue实例上挂载router new Vue({ router, }).$mount('#app'); ``` 4. 在模板中使用 el-menu: ```html <template> <div id="app"> <el-menu :default-active="$route.path" class="el-menu-demo"> <el-menu-item v-for="(item, index) in routes" :key="index" :to="{ path: item.path }"> {{ item.meta.title }} <!-- 如果有meta信息可以用这个展示 --> </el-menu-item> </el-menu> <router-view></router-view> <!-- 渲染当前路由对应的组件 --> </div> </template> <script> export default { data() { return { routes: router.options.routes, // 获取已配置的路由数组 }; }, }; </script> ``` 在这个例子中,`default-active` 属性设置为 `$route.path`,表示默认选中与当前路由匹配的菜单项。当用户点击菜单时,会触发跳转到相应的路由。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值