vue实现一个简单的吸顶、锚点和滚动高亮按钮效果

前言

因公司后台管理系统不少功能技术老旧,最近在用vue重构公司的后台管理系统,在作商品管理添加商品这一块,借鉴淘宝的添加商品的交互,须要实现一个简单的吸顶、锚点和滚动高亮按钮的效果。javascript

需求

  1. 滚动页面到顶部,实现某元素固定到顶部效果
  2. 点击某个按钮,页面滚动到相应的位置
  3. 滚动页面,当到达某个位置时,高亮对应的相关按钮

元素吸顶实现方式

关于元素吸顶效果,经过查阅相关资料和相关测试,有三种方式(还有一种是jquery的方法,这里就不介绍了)html

1、使用position:sticky

1. 什么是position:sticky?

粘性定位元素至关于position:relative和position:sticky的结合体,受限于父级元素,在不一样的条件下呈现出不一样的页面效果vue

2. 如何使用sticky?

sticky元素效果彻底受限于父级元素,使用条件:
1.sticky元素的父元素的overflow只能设置为visible,不然会致使没有粘滞效果
2.sticky元素的父元素不能设置固定的高度,不然会致使没有粘滞效果
3.sticky知足条件变成fixed定位时,与标准fixed元素不同,不会脱离文档流
4.sticky 定位的元素不能添加一个只包含自身的父元素,会致使没有粘滞效果
5.同一个父级元素中的sticky元素,若是定位值相等,则会重叠,若是属于不一样父级元素中,则会挤掉以前的元素,造成依次占位的效果 具体实现效果以下:java

.sticky-box{  
 position: sticky;  
 position: -webkit-sticky;
 top: 60px; //可经过js动态设置
}
复制代码

3.兼容性

经过查看can i use 能够看到相关的兼容性: jquery

能够看出这个属性的兼容性不是那么好,若是项目须要兼容到ie11等的话,就不是那么适用了

2、使用offsetTop

HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离。所以咱们须要注意的是,在监听页面滚动的过程当中,须要将定位父级元素的偏移量也计算在内,能够以下写法:web

//获取当前元素的offsetTop
    getOffsetTop(obj) {
      let offsetTop = 0;
      while (obj != window.document.body && obj != null) {
        offsetTop += obj.offsetTop;
        obj = obj.offsetParent;
      }
      return offsetTop;
    }
复制代码

经过在vue的mounted生命周期函数中添加监听事件滚动的事件:element-ui

mounted() {
    /**经过给变成固定定位的元素添加一个同等高度的父元素,防止该元素变成固定定位时,脱离文档流致使的页面抖动 */
    this.tabsHeight = this.$refs.elTabs.offsetHeight;
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    //离开该页面须要移除这个监听的事件
    window.removeEventListener("scroll", this.handleScroll);
  },
   methods: {
    /**滚动事件 */
    handleScroll() {
        //获取页面滚动条的高度
        let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
        let offsetTop = this.getOffsetTop(this.$refs.elTabs);
        this.isFixed = scrollTop > offsetTop;
    }
  }
复制代码

同时若是这种吸顶方式在项目中会屡次用到,就能够封装成组件的形式浏览器

3、使用getBoundingClientRect().top

还有一种更为直接的方式,能够实现吸顶效果,就是使用getBoundingClientRect().top来获取元素相对于视口(浏览器窗口)的位置,相对于offsetTop,该方法不用考虑到吸顶元素的父级元素和页面滚动条的高度,直接对该元素进行处理便可,实现以下: /*滚动事件 / handleScroll() { / * getBoundingClientRect().top 获取某元素距离浏览器顶部的高度,不包含滚动的距离 */ let tabOffsetTop = this.$refs.stickyBox.getBoundingClientRect().top; this.isFixed = tabOffsetTop < this.offsetTop }性能优化

/**滚动事件 */
    handleScroll() {
      /** * getBoundingClientRect().top 获取某元素距离浏览器顶部的高度,不包含滚动的距离 this.offsetTop 表示的是吸顶元素距离顶部的条件值(通常项目需求是0) */
      let tabOffsetTop = this.$refs.stickyBox.getBoundingClientRect().top;
      this.isFixed = tabOffsetTop < this.offsetTop
    }
复制代码

锚点定位

点击相应的按钮,页面滚动到相应的位置,目前我知道实现该功能的方式有两种:
1. 使用a标签订位
2. 使用js模拟锚点定位bash

使用a标签订位

这是一种常见的定位方式,它有两种实现方式:
1. 经过href属性连接到指定元素的id
2.另外一种是添加一个 a 标签,再将 href 属性连接到这个 a 标签的 name 属性

<a href="#view1">按钮1</a>  
  <a href="#view2">按钮1</a>  
  <div id="view1">视图1</div>  
  <div><a name="view2">视图2</a></div>  
复制代码

这种定位方式很简单,支持任意标签的定位,可是a标签的定位会改变路由的hash,若是有相关路由会进行路由跳转

使用js模拟锚点定位

经过js获取元素的scrollTop值,使其滚动到指定的位置,就能实现锚点定位效果,这里的tab切换选项,用到是的element-ui的el-tabs组件,具体实现以下:

<!-- html -->
 <el-tabs v-model="activeName" type="card" @tab-click="tabClick">
    <el-tab-pane :label="item.name" :name="item.key" v-for="item in tabList" :key="item.key"></el-tab-pane>
</el-tabs>
<!-- js -->
methods:{
  //获取当前元素的offsetTop
  getOffsetTop(obj) {
    let offsetTop = 0;
    while (obj != window.document.body && obj != null) {
    offsetTop += obj.offsetTop;
    obj = obj.offsetParent;
  }
    return offsetTop;
 },
 <!--锚点点击事件-->
 <!--fixedHeight 滚动的位置上方固定的高度-->
 tabClick(e) {
    let _this = this;
    //获取当前选中的index以便后面滚动高亮
    this.index = parseInt(e.index);
    //给定一个标识,锚点事件不触发滚动
    this.isScroll = false;
    this.isChange = false;
    //获取当前选中元素的top值(给元素绑定对应的ref值)
    let offsetTop = this.getOffsetTop(this.$refs[this.activeName]);
    let scrollTop = offsetTop - this.fixedHeight;
    window.scrollTo({
        top: scrollTop
   });
}
复制代码

不得不提的一个方法就是scrollIntoView,Element.scrollIntoView() 方法让当前的元素滚动到浏览器窗口的可视区域内,同时还支持动态效果,可是不支持配置滚动到距离顶部的距离,会出现遮罩现象,可是很适合作会到顶部的功能

滚动高亮按钮

当用户滚动内容区时,高亮距离按钮组件最近的那个元素所对应的按钮。 经过监听滚动事件,获取当前选中的tab的offsetTop值和当前页面的scrollTop值,判断向上或者向下滚动,作出不一样的处理,具体以下:

//页面滚动要作的事情
handleScroll() {
    let scrollTop =
        window.pageYOffset ||
        document.documentElement.scrollTop ||
        document.body.scrollTop;
      this.scrollTop = scrollTop;
      <!--isScroll 用于避免锚点事件触发页面滚动--> if (!this.isScroll) return; /** * scrollTop 页面的滚动条的高度 * offsetTop 当前选中的tab元素的offsetTop * offsetHeight 当前选中元素的高度 */ let offsetTop = this.getOffsetTop(this.$refs[this.activeName]); let offsetHeight = this.$refs[this.activeName].offsetHeight; let actuaTop = scrollTop + this.fixedHeight; let length = this.tabList.length; /** * 页面滚动中根据相应位置变换选中tab */ if (actuaTop < offsetTop && this.index > 0) { this.index = this.index - 1; this.activeName = this.tabList[this.index].key; } else if (this.index < length && actuaTop > offsetTop + offsetHeight) { this.index = this.index + 1; this.activeName = this.tabList[this.index].key; } } 复制代码

性能优化

页面中读取属性会致使页面reflow(下次会对致使页面reflow和repaint 的操做作一个总结),过分的reflow会致使页面性能降低,因此咱们应该尽可能减小reflow的次数,以便给用户更好的体验。 若是产品能够接受效果有延迟,就可使用节流函数控制在必定时间内只执行一次函数(节流函数可使用lodash.js 封装好的 throttle 方法)

总结

写到这里,需求中的三个功能都已经实现,也许还存在更好的方案,可是经过此次实现这三个需求,若是你们有其余更好的方法,欢迎留言补充,但我也从中学习到了一些东西 1.position:sticky的用法和使用条件 2.scrollTop、offsetTop等元素的相关属性、getBoundingClientRect()用法和 scrollTo、scrollIntoView的用法 3.锚点时间和滚动高亮事件致使的冲突处理等

相关文章
相关标签/搜索