vue-防京东详情导航跟随滚动

前段时间 项目要求做类似京东详情导航。今日有空记录哈!

 坑:window.scrollTo({top: 0, behavior: "smooth" }); 不知为何无法生效

解决:dom取包含总体内容的元素。次例子取 let el = this.$refs.wrapper;

大致样式:gif上看着导航的高亮状态闪的很快,实际上并不如此。新手新手不会操作

 大致思路:

step1:先实现导航点击切换到响应锚点
step2:添加页面滚动事件,(注意离开当前页面时要销毁滚动事件,以免切换路由时影响其他路由。。。。

完整代码:

<!--
 * @Author: lingxie
 * @Date: 2020-05-27 14:35:35
 * @Descripttion: 
--> 
<template>
  <div class="model-box">
    <div class="nav-wrap" v-if="isShowNav" ref="navWrap">
      <nav>
        <ul>
          <li v-for="(i,idx) in navList" :key="'nav'+idx">
            <span :class="{'active':curNavIdx==idx}" @click="handleNav(idx)">{{i}}</span>
          </li>
        </ul>
      </nav>
    </div>
    <div class="con" ref="wrapper" id="wrapper">
      <section ref="panel0">
        <h3>商品</h3>
        <p v-for="(i,idx) in 20" :key="'0'+idx">商品</p>
      </section>

      <section ref="panel1">
        <h3>评价</h3>
        <p v-for="(i,idx) in 20" :key="'0'+idx">评价</p>
      </section>

      <section ref="panel2">
        <h3>详情</h3>
        <p v-for="(i,idx) in 10" :key="'0'+idx">详情</p>
      </section>

      <section ref="panel3">
        <h3>推荐</h3>
        <p v-for="(i,idx) in 20" :key="'0'+idx">推荐</p>
      </section>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      isShowNav: false, //是否展示导航
      scrollTop: 0,
      curNavIdx: 0,
      navList: ["商品", "评价", "详情", "推荐"]
    };
  },
  watch:{
    curNavIdx(val){
      console.log('索引',val);
      this.curNavIdx = val;
    }
  },
  mounted() {
    this.$nextTick(() => {
      window.addEventListener("scroll", this.handleScroll, true);
    });
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.handleScroll, true);
  },
  methods: {
    // 点击跳转至相应锚点
    handleNav(idx) {
      this.curNavIdx = idx;
      let el = this.$refs.wrapper;
      let navHeight = this.$refs.navWrap.offsetHeight;
      let panel1T = this.$refs.panel1.offsetTop - navHeight;
      let panel2T = this.$refs.panel2.offsetTop - navHeight;
      let panel3T = this.$refs.panel3.offsetTop - navHeight;
        console.log(panel1T,panel2T,panel3T);
        if( this.curNavIdx==0){
          console.log('进入',0);
          el.scrollTo({top: 0, behavior: "smooth" });
        }
        if( this.curNavIdx==1){
          console.log('进入',1);
          el.scrollTo({top: panel1T, behavior: "smooth" });
        }
        if( this.curNavIdx==2){
           console.log('进入',2);
           el.scrollTo({top: panel2T, behavior: "smooth" });
        }
        if( this.curNavIdx==3){
           console.log('进入',3);
            el.scrollTo({top: panel3T, behavior: "smooth" });
        }
    },
    handleScroll() {
      var el = this.$refs.wrapper;
      let scrollTop = el.scrollTop; // 获取当前的滚动距离
      let clientHeight = el.clientHeight; //获取当前可视区域
      let scrollHeight = el.scrollHeight; //获取当前滚动高度
      // console.log(scrollTop);
      if (scrollTop > 30) {
        this.isShowNav = true;
      } else {
        this.isShowNav = false;
      }
      if (this.isShowNav) {
        // console.log(scrollTop,clientHeight,scrollHeight,';;;;;;;;;;;;;;');
        // 判断是否滚动到底部
        let isBottom = false;
        if (clientHeight + scrollTop === scrollHeight) {
          isBottom = true;
          console.log("滚动到底步了");
        }
        var navE = this.$refs.navWrap;
        if (navE) {
          var navHeight = navE.offsetHeight;
        }
        let panel1T = this.$refs.panel1.offsetTop - navHeight;
        let panel2T = this.$refs.panel2.offsetTop - navHeight;
        let panel3T = this.$refs.panel3.offsetTop - navHeight;
        // console.log(panel1T,panel2T,panel3T,'推荐高度',panelH);
        if (scrollTop < panel1T) {
          if (this.curNavIdx != 0) {
            this.curNavIdx = 0;
          }
        }
        if (scrollTop >= panel1T && scrollTop < panel2T) {
          if (this.curNavIdx != 1) {
            this.curNavIdx = 1;
          }
        }
        if (scrollTop >= panel2T && scrollTop < panel3T) {
          if (this.curNavIdx != 2) {
            this.curNavIdx = 2;
          }
        }
        if (scrollTop >= panel3T || isBottom) {
          if (this.curNavIdx != 3) {
            this.curNavIdx = 3;
          }
        }
      }
    }
  }
};
</script>
<style lang="less" scoped>
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
li{
  list-style: none;
}
.model-box {
  height: 100%;
  .paddingT{
    padding-top: 60px;
  }
  .con {
    // background: #f8f8f8;
    position: relative;
    height: 100%;
    overflow-y: scroll;
    &.paddingT{
      top: 60px;
    }
  }
}
.nav-wrap {
  position: fixed;
  left: 50%;
  top: 0;
   100%;
  transform: translateX(-50%);
  z-index: 99;
  background: lightblue;
  nav {
    height: 40px;
  }
  ul {
    display: flex;
    height: 100%;
    li {
      flex: 1;
      padding-top: 10px;
      text-align: center;
      position: relative;
      span {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        font-size: 14px;
        color: #000;
        &::after {
          display: block;
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          bottom: -10px;
           40px;
          height: 3px;
          content: "";
        }
        &.active {
          &::after {
            background-image: linear-gradient(
              to right,
              rgb(21, 255, 165) 27%,
              rgb(21, 255, 165) 47%,
              rgb(105, 255, 197) 60%,
              rgb(189, 255, 229) 100%
            );
          }
        }
      }
    }
  }
}
section {
  margin-bottom: 40px;
}
</style>

优化滚动事件:

    handleScroll() {
      let el = this.$refs.wrapper;
      let scrollTop = el.scrollTop; // 获取当前的滚动距离
      let clientHeight = el.clientHeight; //获取当前可视区域
      let scrollHeight = el.scrollHeight; //获取当前滚动高度
      // console.log(scrollTop);
      if (scrollTop > 30) {
        this.isShowNav = true;
      } else {
        this.isShowNav = false;
      }
      if (this.isShowNav) {
        // console.log(scrollTop,clientHeight,scrollHeight,';;;;;;;;;;;;;;');
        // 判断是否滚动到底部
        let isBottom = false;
        if (clientHeight + scrollTop === scrollHeight) {
          isBottom = true;
          console.log("滚动到底步了");
        }
        var navE = this.$refs.navWrap;
        if (navE) {
          var navHeight = navE.offsetHeight;
        }
        let panel1T = this.$refs.panel1.offsetTop - navHeight;
        let panel2T = this.$refs.panel2.offsetTop - navHeight;
        let panel3T = this.$refs.panel3.offsetTop - navHeight;
        const offsetTopArr = []
        offsetTopArr.push(0,panel1T,panel2T,panel3T)
        let navIndex = 0
        for (let n = 0; n < offsetTopArr.length; n++) {
          // 若到底了,则当前索引为3
          // 否则导航索引就应该是 n 了
          if (scrollTop >= offsetTopArr[n]) {
             if(!isBottom){
               navIndex = n;
             }else{
               navIndex = 3;
             }
          }
        }
        this.curNavIdx = navIndex
      }
    }
原文地址:https://www.cnblogs.com/lingXie/p/12988642.html