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>

总结

实现方式有很多种,我写的这种仅供参考。如有我写的不清晰的,欢迎私信或文章评论。与大家共同进步