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.錨點時間和滾動高亮事件致使的衝突處理等

相關文章
相關標籤/搜索