因公司后臺管理系統很多功能技術老舊,最近在用vue重構公司的后臺管理系統,在做商品管理添加商品這一塊,借鑒淘寶的添加商品的交互,需要實現一個簡單的吸頂、錨點和滾動高亮按鈕的效果。
需求
元素吸頂實現方式
關于元素吸頂效果,通過查閱相關資料和相關測試,有三種方式(還有一種是jquery的方法,這里就不介紹了)
一、使用position:sticky
1. 什么是position:sticky?
粘性定位元素相當于position:relative和position:sticky的結合體,受限于父級元素,在不同的條件下呈現出不同的頁面效果
2. 如何使用sticky?
sticky元素效果完全受限于父級元素,使用條件:
1.sticky元素的父元素的overflow只能設置為visible,否則會導致沒有粘滯效果
2.sticky元素的父元素不能設置固定的高度,否則會導致沒有粘滯效果
3.sticky滿足條件變成fixed定位時,與標準fixed元素不一樣,不會脫離文檔流
4.sticky 定位的元素不能添加一個只包含自身的父元素,會導致沒有粘滯效果
5.同一個父級元素中的sticky元素,如果定位值相等,則會重疊,如果屬于不同父級元素中,則會擠掉之前的元素,形成依次占位的效果 具體實現效果如下:
.sticky-box{ position: sticky; position: -webkit-sticky; top: 60px; //可通過js動態設置 }
3.兼容性
通過查看can i use 可以看到相關的兼容性:
可以看出這個屬性的兼容性不是那么好,如果項目需要兼容到ie11等的話,就不是那么適用了
二、使用offsetTop**
HTMLElement.offsetTop 為只讀屬性,它返回當前元素相對于其 offsetParent 元素的頂部內邊距的距離。因此我們需要注意的是,在監聽頁面滾動的過程中,需要將定位父級元素的偏移量也計算在內,可以如下寫法:
//獲取當前元素的offsetTop getOffsetTop(obj) { let offsetTop = 0; while (obj != window.document.body && obj != null) { offsetTop += obj.offsetTop; obj = obj.offsetParent; } return offsetTop; }
通過在vue的mounted生命周期函數中添加監聽事件滾動的事件:
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; } }
同時如果這種吸頂方式在項目中會多次用到,就可以封裝成組件的形式
三、使用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模擬錨點定位
使用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.錨點時間和滾動高亮事件導致的沖突處理等
以上所述是小編給大家介紹的vue實現吸頂、錨點和滾動高亮按鈕效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。