乐趣区

关于前端:vue使用原生js实现web端左右滚动联动效果

问题形容

左右联动的成果,在挪动端比拟常见。比方美团外卖中的商家外卖商品抉择。常见的解决方案就是应用 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>

总结

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

退出移动版