这种效果怎么实现?

就是鼠标点击左边的栏目,右边的缓缓的定位到栏目,这种用什么做?有相应的插件还是手写js。
image.png

阅读 3.6k
6 个回答
  1. scrollIntoView,楼上说过了
    参考:

    1. 你用过scrollIntoView吗?
    2. Element.scrollIntoView()
  2. 使用锚点。
    对页面或者容器父元素设置:scroll-behavior: smooth; 实现内容的平滑移动。
  3. 使用 JS 处理。
    scrollTo()

    document.querySelector('.example').scrollTo({
     top: 0,
     left: 0,
     behavior: 'smooth',
    });

//之前遇到收藏了 你看看

<template>
  <div id="linage">
    <div>{{ currentOptions }}</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)"
          ref="jiffje"
        >
          {{ 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="item2">
              <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, // 滚动的距离
      currentOptions: "",
      rightHeigh: [],
    };
  },
  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"],
        },
        {
          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];
        });
         this.rightArr.forEach((v, i) => {
          if (i - 1 == scrollWhichIndex) {
            this.currentOptions = v.titleOne;
          }
        });
        // 初始的区间为-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>
.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: 14px 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>

最简单的办法 直接锚点

一个想法,没具体实验过,通过锚点的 CSS 属性 :target 触发一个 transition 的过渡效果,比如

a#test:target {
    transition: all 200ms linear;
}

只是一个猜想,行不行不知道

推荐问题
宣传栏