摘要:但當該元素的位置移出設置的視圖范圍時,其定位效果將變成,并根據設置的等作為其定位參數。另外,筆者在網上找過相關的組件。面向人群急于使用組件的同學。若頁面大小發(fā)現(xiàn)變化,原顯示的位置可能與頁面變化后的不一致。
sticky簡介
sticky的本意是粘的,粘性的,使用其進行的布局被稱為粘性布局。
sticky是position屬性新推出的值,屬于CSS3的新特性,常用與實現(xiàn)吸附效果。
設置了sticky布局的元素,在視圖窗口時,與靜態(tài)布局的表現(xiàn)一致。
但當該元素的位置移出設置的視圖范圍時,其定位效果將變成fixed,并根據設置的left、top等作為其定位參數。
具體效果如下,當頁面滾動至下方,原本靜態(tài)布局的「演職員表」將變?yōu)閒ixed布局,固定在頁面頂部。
sticky兼容性下圖可見,除了IE以外,目前絕大部分瀏覽器都是支持sticky布局。
需求背景但是實際情況并不如上圖展示的那么美好,在360安全瀏覽器上,并不支持sticky布局,即使使用極速模式(使用chrome內核運行)也不支持。
另外,筆者在網上找過相關的vue-sticky組件。但是使用起來并不是那么順手,而且看其源碼也是一頭霧水,用著不踏實。
所以自己寫了一個,希望通過本文能將組件分享出去,也希望將本組件的原理講清楚。讓其他同學在使用的時候能更踏實一些。遇到坑也知道該怎么去填。希望能幫到大家。
面向人群急于使用vue-sticky組件的同學。直接下載文件,拷貝代碼即可運行。
喜歡看源碼,希望了解組件背后原理的同學。
其實本sticky組件原理很簡單,看完本文,相信你一定能把背后原理看懂。
剛接觸前端的同學也可以通過本文章養(yǎng)成看源碼的習慣。打破對源碼的恐懼,相信自己,其實看源碼并沒有想象中的那么困難
技術難點
sticky效果需要解決這么幾個問題
占位問題,sticky實現(xiàn)原理,無非是在特定超出視圖時,將內容的布局設為fixed。但將內容設置為fixed布局時,內容將脫離文檔流,原本占據的空間將被釋放掉,這將導致頁面空了一塊后其他內容發(fā)生位移。
頁面resize后位置問題。當使用fixed定位時,其定位將根據頁面進行。若頁面大小發(fā)現(xiàn)變化,原顯示的位置可能與頁面變化后的不一致。這時需要重新設置。
橫向滾動條問題。本質上和resize是同一個問題,需要監(jiān)聽scroll事件,當頁面發(fā)送無相關方向的位移時,需要重新計算其位置,例如前面的sticky效果示例中設置了「演職員表」的top值,當其fixed后,滾動X軸,需要重新設置fixed的left參數。讓元素始終位于頁面相同位置
實現(xiàn)思路
組件有兩層容器
一個是內容slot的容器$content
一個是內容容器$content的sticky盒子容器$box
即包圍關系為$sticky-box($content(slot))
監(jiān)聽vue的mounted事件
這時內容slot已經被渲染出來
獲取slot容器$content的寬高,設置到$box容器上
設置$box容器寬高是為了當后續(xù)$content容器Fixed后,$box容器仍在頁面中占據空間。
const style = window.getComputedStyle(this.$refs.$content) this.boxStyle.width = style.width this.boxStyle.height = style.height
監(jiān)聽scroll事件
在事件中獲取容器$content在頁面中的位置,并將其與預設值進行大小比較,判斷$content是否應該fixed
怎么便捷地獲取$content在頁面中的位置呢?直接使用Element.getBoundingClientRect()函數,該函數將返回{left,top}分別表示dom元素距離窗口的距離。詳細可參看MDN文檔
const { $content, $box } = this.$refs const { contentStyle } = this const boxTop = $box.getBoundingClientRect().top const boxLeft = $box.getBoundingClientRect().left const contentTop = $content.getBoundingClientRect().top const contentLeft = $content.getBoundingClientRect().left
比較boxTop與預設值top的大小,當boxTop比預設值值要小時,即內容即將移出規(guī)定的視圖范圍。這時將內容容器$content設置為fixed。并設置其top值(即預設的top值,吸頂距離),left值與盒子位置相同,故設置為盒子距離的left值
當boxTop比預設值值要大時,即內容重新返回的視圖范圍。則將內容容器$content重新設置會靜態(tài)布局,讓其重新回到盒子布局內部。由于靜態(tài)布局不受left和top的影響,所以不需要設置left和top
if (boxTop > parseInt(this.top) && this.isFixedY) { contentStyle.position = "static" } else if (boxTop < parseInt(this.topI) && !this.isFixedY) { contentStyle.position = "fixed" contentStyle.top = this.top contentStyle.left = `${boxLeft}px` }
在scroll事件中,除了Y軸方向上的滾動,還可能發(fā)生X軸方向的滾動。這些需要重新設置fixed元素的left值,讓其與盒子容器的left值一致
// 當位置距左位置不對時,重新設置fixed對象left的值,防止左右滾動位置不對問題 if (contentLeft !== boxLeft && this.left === "unset") { const { $box } = this.$refs const { contentStyle } = this const boxTop = $box.getBoundingClientRect().top const boxLeft = $box.getBoundingClientRect().left if (contentStyle.position === "fixed") { contentStyle.top = this.top contentStyle.left = `${boxLeft}px` } }
最后,是監(jiān)聽頁面的resize事件,防止頁面大小變化時,fixed相對頁面的變化。同樣的,重新設置left值
// 當位置距左位置不對時,重新設置fixed對象left的值,防止左右滾動位置不對問題 const { $box } = this.$refs const { contentStyle } = this const boxTop = $box.getBoundingClientRect().top const boxLeft = $box.getBoundingClientRect().left if (contentStyle.position === "fixed") { contentStyle.top = this.top === "unset" ? `${boxTop}px` : this.top contentStyle.left = this.left === "unset" ? `${boxLeft}px` : this.left }需要注意的地方
目前僅支持top與left值的多帶帶使用,暫不支持同時設置
目前僅支持px單位,暫不支持rem及百分比單位
設置內容樣式時需要注意,設置定位相關屬性需要設置在box容器上,例如設置"displCy: inline-block;","verticCl-Clign: top;","margin"
設置外觀樣式,如背景,邊框等,則設置在slot內容中
即內容content-box以外的設置在box容器中,content-box以內的樣式,則設置在slot內容中
盒子容器不需要設置position屬性,即使有也會被沖刷掉。因為程序將內部重新設置position的值
同樣的,在樣式中設置盒子容器的left和top值也是無效的,會被程序內部重新設置。只能通過dom屬性值傳遞到組件中進行設置
后續(xù)優(yōu)化目前本組件僅實現(xiàn)了基本功能,后續(xù)還將繼續(xù)優(yōu)化以下功能
slot內容中,如果有圖片,如果獲取設置寬高,(監(jiān)聽所有圖片的load事件,重新設置容器的高寬)
目前僅在mounted中獲取slot的寬高,這僅僅是dom元素被渲染,但是dom內容是否加載完畢并不知道的,如img標簽,后續(xù)在slot中,監(jiān)聽所有img標簽的load事件,load中,重新設置組件容器的大小
slot內容有變化時,設置容器
同樣的,當slot內容變化后,重新設置$content的寬高
具體如何實現(xiàn),暫時還沒有頭緒
移動端適配
目前只測試了在PC中的效果,暫未在移動端做測試。不排除移動端使用存在坑
單位適配
目前只支持PX單位,未支持rem,百分百等單位
left和top值的混合使用,目前只支持單個屬性的使用,暫不支持同時設置
項目源碼及示例 第一稿寫完了,撒花花文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98139.html
摘要:父子組件通信兄弟組件通信跨級組件通信父傳子子組件用接收,父組件用發(fā)送父組件紅樓夢西游記三國演義水滸傳子組件子傳父子組件用有的版本名稱只能小寫發(fā)送,父組件自定義事件然后在方法中接收父組件不能加括號子組件點擊把傳給父組件可以傳 父子組件通信: props、 $parent / $children、 provide / inject 、 ref 、 $attrs / $listeners ...
摘要:在前端基礎進階八深入詳解函數的柯里化一文中,我有分享柯里化相關的知識。雖然說高階組件與柯里化都屬于比較難以理解的知識點,但是他們組合在一起使用時并沒有新增更多的難點。 可能看過我以前文章的同學應該會猜得到當我用New的方法來舉例學習高階組件時,接下來要分享的就是柯里化了。高階組件與函數柯里化的運用是非常能夠提高代碼逼格的技巧,如果你有剩余的精力,完全可以花點時間學習一下。 在前端基礎進...
摘要:前面有講到過很多頁面會在初始時驗證登錄狀態(tài)與用戶角色。這個時候就涉及到一個高階組件的嵌套使用。而每一個高階組件函數執(zhí)行之后中所返回的組件,剛好可以作為下一個高階組件的參數繼續(xù)執(zhí)行,而并不會影響基礎組件中所獲得的新能力。 前面有講到過很多頁面會在初始時驗證登錄狀態(tài)與用戶角色。我們可以使用高階組件來封裝這部分驗證邏輯。封裝好之后我們在使用的時候就可以如下: export default w...
摘要:事先將狀態(tài)更新的規(guī)則寫好,然后將規(guī)則注入到組件中,然后狀態(tài)按照預訂的規(guī)則來進行更新。主動型和被動型規(guī)則內置了這幾種常見的規(guī)則,除了規(guī)則外,其他都是屬于主動型更新規(guī)則,在達到一定的條件上會自動觸發(fā)狀態(tài)更新。 Vuet.js是什么? Vuet.js是給Vue.js提供狀態(tài)管理的一個工具,與vuex不同,它是一種崇尚規(guī)則定制的狀態(tài)管理模式。事先將狀態(tài)更新的規(guī)則寫好,然后將規(guī)則注入到組件中,然...
閱讀 3603·2021-11-24 10:25
閱讀 2508·2021-11-24 09:38
閱讀 1217·2021-09-08 10:41
閱讀 2904·2021-09-01 10:42
閱讀 2569·2021-07-25 21:37
閱讀 1981·2019-08-30 15:56
閱讀 914·2019-08-30 15:55
閱讀 2750·2019-08-30 15:54