Vue框架下的原生JS实现网页左右滚动联动效果
发表时间: 2023-12-08 10:40
左右联动的效果,在移动端比较常见。比如美团外卖中的商家外卖商品选择。常见的解决方案就是使用better-scroll滑屏库去实现。不过偶尔web端的项目也会要做这样的左右联动的效果。本篇文章是在vue框架中使用原生js来实现相应的效果的。我们先看一下最终的效果图:
代码中的注释写的有思路步骤的。请按照注释思路步骤来
<template> <div id="app"> <div class="top"> <h2>vue使用原生js实现web端左右滚动联动效果</h2> </div> <div class="bottom"> <!-- 左侧菜单栏 --> <div class="bottomLeft"> <div class="leftItem" v-for="(item0, index0) in leftArr" :key="index0" :class="{ highLight: whichIndex == index0 }" @click="letItemHighLight(index0)" > {{ item0 }} </div> </div> <!-- 左侧菜单栏对应的右侧的内容 --> <div class="bottomRight" ref="wrapper"> <div class="bottomRightContent" v-for="(item, index) in rightArr" :key="index" ref="item" > <div class="bottomRightContentHead">{{ item.titleOne }}</div> <div class="bottomRightContentBody"> <el-col :span="8" v-for="(item2, index2) in item.titleTwo" :key="index2"> <span class="circle"></span> <span class="word">{{ item2 }}</span> </el-col> <!-- 清除一下浮动 --> <div style="clear: both"></div> </div> <div class="bottomRightContentFooter"></div> </div> </div> </div> </div></template><script>export default { data() { return { whichIndex: 0, // 动态显示左侧菜单栏高亮 leftArr: [], // 左侧菜单栏的数据 rightArr: [], // 右侧详情展示对应的数据 rightHeightArr: [], // 右侧每一项的高度数组 rightHeightSumArr: [], // 右侧每一项的高度累加数组 r: 0, // 滚动的距离 }; }, mounted() { // 第一步,先发请求获取左右两侧的数据,用于渲染出页面,这里我们模拟一下发请求的数据 this.getLeftArrData(); this.getRightArrData(); }, methods: { getLeftArrData() { let apiLeftArr = [ "西游记", "三国演义", "红楼梦", "水浒传", "龙族", "幻城", "犬夜叉", "海贼王", "一拳超人", "金刚狼", "钢铁侠", "灭霸", "雷神", "贪玩蓝月", "梦幻西游", "王者荣耀", ]; this.leftArr = apiLeftArr; }, getRightArrData() { let apiRightArr = [ { titleOne: "西游记", titleTwo: ["111", "222", "333", "444"], }, { titleOne: "三国演义", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "红楼梦", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "水浒传", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "龙族", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "幻城", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "犬夜叉", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "海贼王", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "一拳超人", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "金刚狼", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "钢铁侠", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "灭霸", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "雷神", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "贪玩蓝月", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "梦幻西游", titleTwo: ["111", "222", "333", "444", "111", "222", "333", "444"], }, { titleOne: "王者荣耀", titleTwo: [ "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", "111", "222", "333", "444", ], }, ]; this.rightArr = apiRightArr; // 第二步,左右两侧有数据以后,才会把高度撑起来,才可以计算高度数组。注意先后顺序 // 使用this.$nextTick()将回调,也就是计算两个高度数组,延迟到下次 DOM 更新循环之后再计算 this.$nextTick(() => { this.getTwoHeightArr(); }); }, getTwoHeightArr() { // console.log("可能为空", this.$refs.item); this.$refs.item.forEach((item) => { this.rightHeightArr.push(item["offsetHeight"]); }); let num = 0; this.rightHeightArr.forEach((item) => { num = num + item; this.rightHeightSumArr.push(num); }); // 第三步,有了高度滚动条以后,就可以绑定滚动事件了 this.bindScrollEvent(); }, bindScrollEvent() { // 第四步,绑定滚动事件,看滚动到那个区间里面,思路就是通过右侧的区间去同步左侧的区间 this.$refs.wrapper.onscroll = () => { this.r = this.$refs.wrapper.scrollTop; // 看看浏览器滚动的高度落到那个区间,在那个区间,就让对应的项高亮 const scrollWhichIndex = this.rightHeightSumArr.findIndex((item, index) => { return ( this.r >= this.rightHeightSumArr[index] && this.r < this.rightHeightSumArr[index + 1] ); }); console.log("所在区间",scrollWhichIndex); // 初始的区间为-1,所以还让其为第一项,即索引为0,当用户往下滑动的时候,所以就会 // 一直大于负一,所以就让其加上一和左侧的高亮项对应。 if (scrollWhichIndex > -1) { this.whichIndex = scrollWhichIndex + 1; } else { this.whichIndex = 0; } } }, // 第五步,当用户点击的时候再让其滚动,因为滚动和高亮是关联的,所以只要控制滚动,就相当于控制高亮。 // 滚动的距离就是,看用户点击的是哪个菜单项的索引,通过索引找到累加数组对应的那一项, // 也就是滚动的距离。当为第一项的时候边界值要控制一下 letItemHighLight(i) { if (this.rightHeightSumArr[i - 1] == undefined) { this.$refs.wrapper.scrollTop = 0; } else { this.$refs.wrapper.scrollTop = this.rightHeightSumArr[i - 1]; } }, },};</script><style lang="less" scoped>#app { width: 100%; height: 100vh; .top { width: 100%; height: 80px; text-align: center; line-height: 80px; background-color: #e9e9e9; } .bottom { width: 100%; height: calc(100% - 80px); display: flex; .bottomLeft { width: 288px; height: 100%; background-color: #eee; .leftItem { width: 100%; height: 50px; line-height: 50px; text-align: center; cursor: pointer; } .leftItem:hover { background-color: #dfe3f1; } .highLight { background: #dfe3f1; } } .bottomRight { width: calc(100% - 288px); height: 100%; box-sizing: border-box; padding: 36px 36px 0 36px; overflow-y: auto; .bottomRightContent { width: 100%; box-sizing: border-box; padding-bottom: 36px; .bottomRightContentHead { height: 25px; font-family: PingFang SC; font-style: normal; font-weight: 600; font-size: 24px; line-height: 25px; text-transform: capitalize; color: rgba(0, 0, 0, 0.85); margin-bottom: 32px; } .bottomRightContentBody { .el-col { position: relative; margin-bottom: 18px; .circle { display: inline-block; width: 6px; height: 6px; background: #4677f6; border-radius: 50%; position: absolute; top: 8px; left: 0; } .word { margin-left: 12px; font-family: PingFang SC; font-style: normal; font-weight: normal; font-size: 14px; color: #4677f6; cursor: pointer; } .word:hover { text-decoration: underline; } .topPlace { position: absolute; top: 1px; margin-left: 8px; } } } .bottomRightContentFooter { height: 1px; width: 100%; margin-top: 14px; background-color: #e9e9e9; } } } }}</style>
实现方式有很多种,我写的这种仅供参考。如有我写的不清晰的,欢迎私信或文章评论。与大家共同进步