美文网首页
Vue2实现电梯导航(内容随锚点滚动+动画)

Vue2实现电梯导航(内容随锚点滚动+动画)

作者: 哈士奇的猫 | 来源:发表于2023-08-21 10:21 被阅读0次
    最终实现效果

    实现效果说明

    整体布局,内容和导航联动起来,内容滚动时电梯导航栏跟随内容指定对应楼层,点击导航,滚动到指定内容。

    代码实现比较简单,就那么几行,就不多废话了,上代码。

    <template>
      <div class="main">
        <!-- 内容区域  每个模块的id要与menu的id一一对应-->
        <section class="content" id="gonglu">
          <div class="header">
            公路运输
          </div>
        </section>
        <section class="content" id="tielu">
          <div class="header">
            铁路运输
          </div>
        </section>
        <section class="content" id="shuilu">
          <div class="header">
            水路运输
          </div>
        </section>
        <section class="content" id="cangchu">
          <div class="header">
            仓储统配
          </div>
        </section>
        <!-- 侧边栏 -->
        <ul class="sidebar">
          <li v-for="item in menu" :key="item.name" :class="{ active: 1 === item.active }">
            <a href="javascript:;" @click="scrollTo(item)">{{ item.name }}</a>
          </li>
        </ul>
    
      </div>
    </template>
    
    <script>
    export default {
      props: {},
      data() {
        return {
          menu: [
            {
              name: '公路运输',
              id: 'gonglu',
              active: 0, // 用来判断侧边栏的激活状态,为1则代表激活,class的active为true。
            },
            {
              name: '铁路运输',
              id: 'tielu',
              active: 0,
            },
            {
              name: '水路运输',
              id: 'shuilu',
              active: 0,
            },
            {
              name: '仓储统配',
              id: 'cangchu',
              active: 0,
            }
          ]
        }
      },
      mounted() {
        //获取每个模块的参数,因为模块在整页渲染完毕后,offsetTop和scrollHeight值已经是确立的,所以在滚动监听之前可以先获取,暂存在menu中。
        this.menu.forEach(item => {
          item.offsetTop = document.getElementById(item.id).offsetTop;
          item.scrollHeight=document.getElementById(item.id).scrollHeight;
        })
        // 监听滚动事件
        window.addEventListener('scroll', this.onScroll)
      },
      destroy() {
        // 必须移除监听器,不然当该vue组件被销毁了,监听器还在就会出错
        window.removeEventListener('scroll', this.onScroll)
      },
      methods: {
        // 滚动监听器
        onScroll() {
         // 获取浏览器窗口高度
          let clientHeight=window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
          // 获取当前文档流的 scrollTop
          let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
          // 屏幕垂直中线
          let screenMiddle=scrollTop+clientHeight/2;
          this.menu.forEach(item=>{
            //以浏览器窗口垂直中线为准,中线在内容模块的上边界至下边界之间,导航栏都认为在该楼层
            if(screenMiddle-item.offsetTop>=0&&screenMiddle-item.offsetTop<item.scrollHeight){
              item.active=1;
            }else{
              item.active=0;
            }
          })
        },
        // 跳转到指定索引的元素
        scrollTo(item) {
          // 获取目标的 offsetTop
          // css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
          const targetOffsetTop = document.getElementById(item.id).offsetTop
          // 获取当前 offsetTop
          let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
          // 定义一次跳 50 个像素,数字越大跳得越快,但是会有掉帧得感觉,步子迈大了会扯到蛋
          const STEP = 50
          // 判断是往下滑还是往上滑
          if (scrollTop > targetOffsetTop) {
            // 往上滑
            smoothUp()
          } else {
            // 往下滑
            smoothDown()
          }
          // 定义往下滑函数
          function smoothDown() {
            // 如果当前 scrollTop 小于 targetOffsetTop 说明视口还没滑到指定位置
            if (scrollTop < targetOffsetTop) {
              // 如果和目标相差距离大于等于 STEP 就跳 STEP
              // 否则直接跳到目标点,目标是为了防止跳过了。
              if (targetOffsetTop - scrollTop >= STEP) {
                scrollTop += STEP
              } else {
                scrollTop = targetOffsetTop
              }
              document.body.scrollTop = scrollTop
              document.documentElement.scrollTop = scrollTop
              // 关于 requestAnimationFrame 可以自己查一下,在这种场景下,相比 setInterval 性价比更高
              requestAnimationFrame(smoothDown)
            }
          }
          // 定义往上滑函数
          function smoothUp() {
            if (scrollTop > targetOffsetTop) {
              if (scrollTop - targetOffsetTop >= STEP) {
                scrollTop -= STEP
              } else {
                scrollTop = targetOffsetTop
              }
              document.body.scrollTop = scrollTop
              document.documentElement.scrollTop = scrollTop
              requestAnimationFrame(smoothUp)
            }
          }
        }
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style lang="scss" scoped>
    
    .main {
      padding: 30px;
      padding-right: 240px;
    
      .content {
        height: 70vh;
        background: #eee;
        border-radius: 8px;
        margin-bottom: 15px;
      }
    
      .header {
        padding: 15px;
        font-size: 20px;
        font-weight:600;
        color:black
      }
    }
    
    .sidebar {
      position: fixed;
      top: 50%;
      right: 50px;
      width: 168px;
      overflow: hidden;
      background-color: #fff;
      border-radius: 8px;
      padding: 0;
      transform: translateY(-50%);
      box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.05), 0px 3px 5px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px -1px rgba(0, 0, 0, 0.04);
    
      li {
        line-height: 40px;
        text-align: center;
        list-style: none;
    
        &:not(:last-child) {
          border-bottom: #f5f5f5 solid 1px;
        }
    
        &.active {
          color: #fff;
          background-color: #ff8000;
        }
    
        a {
          display: block;
          width: 100%;
          height: 100%;
          color: #333;
          text-decoration: none;
        }
      }
    }
    </style>
    
    

    相关文章

      网友评论

          本文标题:Vue2实现电梯导航(内容随锚点滚动+动画)

          本文链接:https://www.haomeiwen.com/subject/zcgemdtx.html