仿知乎步骤条导航(我自己起的名字)

最近项目中有需求需要做一个类似于步骤条的导航,从使用vue以后,基本上element ui实现不了的效果我都偏爱自己写,算是一个积累吧,之后有类似需求可以直接拿过来用。

还记得当年jquery的时候,那个插件库是真的全,自己啥效果也不想写,需求来了直接找,我那里边现在应该还有100多块钱的jq币哈哈哈。

原项目中根据实际需求还加了媒体查询对不同分辨率的适配,比如超出1440收起导航,显示展开按钮,大家可以根据实际需求改动做成组件。

废话不多说了,贴代码,一些文字说明还是写在了注释里

<template>
  <div class="wrapper">
    <div class="content">
      <!-- 内容 -->
      <ul>
        <li v-for="({id,title},index) in menuList"
            :key="index"
            :id="id">
          <p>{{title}}</p>
          <div>内容区</div>
        </li>
      </ul>
      <!-- 右侧导航 -->
      <div class="float_nav_wrapper">
        <div class="float_nav">
          <span v-for="({id,title},index) in menuList"
                :key="index"
                @click="floatNavScroll(id)"
                :class="{active:id===activeMenuId}">{{title}}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      // 菜单列表
      menuList: [
        {
          id: '1-1',
          title: '导航1-1'
        },
        {
          id: '1-2',
          title: '导航1-2'
        },
        {
          id: '1-3',
          title: '导航1-3'
        },
        {
          id: '1-4',
          title: '导航1-4'
        },
        {
          id: '1-5',
          title: '导航1-5'
        },
        {
          id: '1-6',
          title: '导航1-6'
        },
        {
          id: '1-7',
          title: '导航1-7'
        },
        {
          id: '1-8',
          title: '导航1-8'
        },
      ],
      // 当前显示的导航栏id
      activeMenuId: '1-1',

    }
  },
  methods: {
    // 点击菜单跳转到指定位置
    floatNavScroll (id) {
      this.activeMenuId = id
      // 获取元素距离顶部的位置
      let scrollTop = document.getElementById(id).offsetTop
      // behavior: 'smooth'是平滑滚动,有兼容性问题
      window.scrollTo({ top: scrollTop, behavior: 'smooth' })
      // 也可以用这种方法,但是我实际项目是粘性定位,需要在scrollTop的基础上增加几个像素,避免粘性布局元素压下去
      // document.getElementById(id).scrollIntoView({ block: 'start', behavior: 'smooth' })
    },
    // 绑定window滚动事件,内容区滚动右侧导航联动
    bindWindowEvent () {
      let timeOut = null // 做一个防抖,避免一直触发
      window.onscroll = () => {
        if (timeOut !== null) {
          clearTimeout(timeOut)
        }
        timeOut = setTimeout(() => {
          for (let i = 0; i < this.menuList.length; i++) {
            let element = this.menuList[i];
            // 判断元素是否在视口内,如果在就把靠前的元素选中
            if (document.documentElement.scrollTop <= document.getElementById(element.id).offsetTop) {
              this.activeMenuId = element.id
              break
            }

          }
        }, 100);
      }
    }
  },
  mounted () {
    this.bindWindowEvent()
  },
}
</script>

<style lang="scss" scoped>
.wrapper {
  width: 100%;
  background-color: #f0f0f0;
  .content {
    width: 1200px;
    padding: 20px;
    margin: 0 auto;
    background-color: #fff;
    ul {
      li {
        list-style: none;
        // height: 220px;
        width: 100%;
        border: 1px solid #f2f2f2;
        border-radius: 10px;
        margin-bottom: 20px;
        p {
          border-radius: 10px 10px 0 0;
          background-color: aquamarine;
          text-align: center;
          font-size: 16px;
          font-weight: 900;
          height: 40px;
          border-bottom: 1px solid #f2f2f2;
          line-height: 40px;
        }
        div {
          width: 100%;
          height: 400px;
          line-height: 400px;
        }
      }
    }
    .float_nav_wrapper {
      z-index: 99;
      box-sizing: border-box;
      padding: 40px 20px;
      border-radius: 10px;
      max-height: 80vh;
      overflow-y: auto;
      width: 200px;
      position: fixed;
      top: 10vh;
      right: 10px;
      .float_nav {
        position: relative;
        display: flex;
        padding-bottom: 14px;
        flex-direction: column;
        border-left: 2px solid rgba(133, 144, 166, 0.12);
        & > span {
          font-size: 14px;
          color: #666;
          font-weight: 500;
          display: inline-block;
          position: relative;
          height: 20px;
          width: 100px;
          line-height: 20px;
          margin-top: 14px;
          cursor: pointer;
          &.active {
            color: rgb(0, 102, 255);
            &:after {
              background-color: rgb(0, 102, 255);
            }
          }
          &::after {
            content: "";
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 8px;
            background-color: rgb(133, 144, 166);
            position: absolute;
            top: 6px;
            left: -5px;
          }
        }
        &:after {
          content: "";
          display: inline-block;
          width: 8px;
          height: 8px;
          border-radius: 8px;
          border: 1px solid rgb(133, 144, 166);
          position: absolute;
          top: -10px;
          left: -5.6px;
        }
        &:before {
          content: "";
          display: inline-block;
          width: 8px;
          height: 8px;
          border-radius: 8px;
          border: 1px solid rgb(133, 144, 166);
          position: absolute;
          bottom: -10px;
          left: -5.6px;
        }
      }
    }
  }
}
</style>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值