导航锚点小试牛刀

14 篇文章 0 订阅
6 篇文章 0 订阅

Offsettop 属性&导航锚点的实现

记录一些视窗高度和滚动的一些属性以及导航菜单跟随内容变动以及点击锚点展示内容

offsetParent 介绍:

offsetparent图片来源网络

偏移量属性

scrollHeight 和 scrollWidth ,可滚动的绝对高度,包括隐藏不可见的部分;scrollTop 和 scrollLeft :可滑动的元素,即元素出现滚动条时,内部在xy轴上滑动的距离,并且可以为其赋值,如果一个元素的内容没有产生垂直方向的滚动条,那么它的scrollTop值为0。

Vue组件简单实现导航栏锚点

<template>
  <div>
    <!-- 内容区域 -->
    <div class="content">
      <div>
        content-0
      </div>
      <div>
        content-1
      </div>
      <div>
        content-2
      </div>
      <div>
        content-3
      </div>
      <div>
        content-4
      </div>
    </div>
    <!-- 导航区域 -->
    <ul class="navs">
      <li :class="{active: active===0}" @click="scrollTo(0)">
        content-0
      </li>
      <li :class="{active: active===1}" @click="scrollTo(1)">
        content-1
      </li>
      <li :class="{active: active===2}" @click="scrollTo(2)">
        content-2
      </li>
      <li :class="{active: active===3}" @click="scrollTo(3)">
        content-3
      </li>
      <li :class="{active: active===4}" @click="scrollTo(4)">
        content-4
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    props: {},
    data () {
      return {
        active: 0, // 当前激活的导航索引
      };
    },
    mounted () {
      // 监听滚动事件
      window.addEventListener('scroll', this.onScroll, false);
    },
    destroy () {
      // 必须移除监听器,不然当该vue组件被销毁了,监听器还在就会出错
      window.removeEventListener('scroll', this.onScroll);
    },
    methods: {
      // 滚动监听器
      onScroll () {
        // 获取所有锚点元素
        const navContents = document.querySelectorAll('.content div');
        // 所有锚点元素的 offsetTop
        const offsetTopArr = [];
        navContents.forEach(item => {
          offsetTopArr.push(item.offsetTop);
        });
        // 获取当前文档流的 scrollTop
        const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        // 定义当前点亮的导航下标
        let navIndex = 0;
        for (let n = 0; n < offsetTopArr.length; n++) {
          // 如果 scrollTop 大于等于第n个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
          // 那么此时导航索引就应该是n了
          if (scrollTop >= offsetTopArr[n]) {
            navIndex = n;
          }
        }
        this.active = navIndex;
      },
      // 跳转到指定索引的元素
      scrollTo (index) {
        // 获取目标的 offsetTop
        // css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
        const targetOffsetTop = document.querySelector(`.content div:nth-child(${index + 1})`).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>

<style scoped>
  /* 内容区的样式 */
  .content {
    background-color: white;
    width: 500px;
  }
  .content div {
    width: 100%;
    height: 600px;
    font-size: 36px;
    padding: 20px;
    background-color: #7ec384;
  }
  .content div:nth-child(2n) {
    background-color: #847ec3;
  }
  /* 导航栏的样式 */
  .navs {
    position: fixed;
    top: 80px;
    left: 700px;
    background-color: #efefef;
  }
  .navs li {
    padding: 0 20px;
    line-height: 1.6;
    font-size: 24px;
  }
  /* 当导航被点亮后改变颜色 */
  .navs .active{
    color: #847ec3;
    background-color: #e2e2e2;
  }
</style>

受到启发,暨大官网的那个帆船跟着标题高亮走的效果也试了一下,纯h5写

 <div style="display: inline-block;">
          <ul id="nav">
            <li class="nav-li">
              <a href="#">学校概况</a>                            
            </li>                            
            <li class="nav-li">
                <a href="#">组织机构</a>                            
            </li>                            
            <li class="nav-li">
                <a href="#">招生就业</a>                            
            </li>                            
            <li class="nav-li">
                <a href="https://faculty.jnu.edu.cn">人才培养</a>                                
                <div class="arrow-top"></div>                            
            </li>                            
            <li class="nav-li">
                <a href="https://hrdam.jnu.edu.cn/298/list.htm">人才招聘</a>                            
            </li>                           
            <li class="nav-li">
                <a href="#">科学研究</a>                            
            </li>                            
            <li class="nav-li">
                <a href="https://wsc.jnu.edu.cn/">合作交流</a>                            
            </li>                            
            <li class="nav-li">
                <a href="#">综合服务</a>                            
            </li>          
          </ul>
        </div>
      </div>

样式,自己随便写写

#nav {
  padding-left: 130px;
  height: 40px;
  line-height: 40px;
  /* margin: 0 auto; */
  display: flex;
  white-space: nowrap;
  /* background: url(./img/hw_zxdj.png) left no-repeat;
  background-size: auto 100%;
  background-position: 100px 0px; */
}

#nav .nav-li {
  flex: 1;
  padding-right: 20px;
  /* width: 12.5%; */
}

#nav li {
  display: inline-block;
  padding-left: 10px;
  height: 40px;
  font-size: 18px;
  text-align: left;
  z-index: 990;
}

#nav li a {
  position: relative;
  float: left;
  width: 100%;
  text-decoration: none;
  color: #fff;
  z-index: 999;
}

.arrow-top {
  position: absolute;
  top: -80px;
  width: 150px;
  height: 80px;
  background: url(./img/top_ship.png) bottom left no-repeat;
  background-size: 100%;
  z-index: 998;
  background-position: -26px 10px;
  transition: all 1s ease-in-out;
}

操作控制offsetLeft 有些层级可能不太对,帆船图片也要自己找

function handleMoveShip () {
      let liList = document.querySelectorAll("#nav li");
      let listContent = document.querySelector(".menu-nav");
      let dropBlock = document.querySelectorAll(".drop-block ul");
      let ship = document.getElementsByClassName("arrow-top")[0];
      let dropList = document.getElementsByClassName("menu-block")[0];
      for(let i= 0, len = liList.length; i < len; i++) {
        // 
        liList[i].addEventListener('mouseover', function(event) {
          console.log('mouseover', event);
          ship.style.left = liList[i].offsetLeft + 'px';
          dropList.style.display = 'block';
          dropBlock[i].setAttribute('class', 'active');
        })
      }
      liList.forEach((item,index) => {
        item.addEventListener('mouseleave', () => {
          dropBlock[index].setAttribute('class', '');
        })
      })

      listContent.addEventListener('mouseleave', function (event) {
        dropList.style.display = 'none';
      })
      dropBlock.forEach((item, index) => {
        item.addEventListener('mouseover', (e) => {
          item.setAttribute('class', 'active');
          ship.style.left = liList[index].offsetLeft + 'px';
        })
      })
      dropBlock.forEach(item => {
        item.addEventListener('mouseleave', () => {
          item.setAttribute('class', '');
        })
      })
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值