摘要:這個(gè)界限就是該元素頂部距離窗口頂部的距離等于該元素設(shè)置的值比如以下像素分割線當(dāng)我的頂部距離窗口頂部為值時(shí),我就會(huì)像一樣在距離窗口值處的時(shí)代發(fā)送分效果圖當(dāng)頁(yè)面滾動(dòng)到距離黃色區(qū)塊頂部時(shí),黃色區(qū)塊就會(huì)在窗口頂部處,頁(yè)面再往下滾動(dòng)距離也不會(huì)變。
position: sticky;
fixed 吸頂
頁(yè)面滾動(dòng)結(jié)束后頁(yè)面才渲染
需求經(jīng)常會(huì)有這樣的需求,當(dāng)頁(yè)面滾動(dòng)到某一個(gè)位置fixedTopValue時(shí),需要某個(gè)元素fixedElement固定在屏幕頂部。基本方法是獲取頁(yè)面的scrollTop值做判斷:
如果 scrollTop > fixedTopValue; 則添加position:fixed;top: 0;否則刪除position:fixed;屬性。
當(dāng)在pc瀏覽器操作的時(shí)候正常。真機(jī)測(cè)試時(shí)總會(huì)出現(xiàn)千奇百怪的現(xiàn)象。比如:
1、 當(dāng)頁(yè)面往下滾時(shí),fixedElement需要等頁(yè)面滾動(dòng)停止之后才會(huì)出現(xiàn)。
2、往上滾動(dòng)時(shí)出現(xiàn)到固定的位置時(shí)不恢復(fù)原樣,而是到達(dá)頂部、等頁(yè)面停止?jié)L動(dòng)之后才會(huì)唰的一下恢復(fù)原樣
3、滾動(dòng)到頂部之后,會(huì)出現(xiàn)兩個(gè)一樣的fixedElement, 過(guò)一會(huì)才恢復(fù)正常。
這樣的用戶體驗(yàn)真的很差,所以迫切需要解決這個(gè)問(wèn)題。
解決方法主要涉及一下三個(gè)方面
1、使用新的定位屬性 position: sticky; (如果支持)
2、如果不支持1,使用window.requestAnimationFrame方法確保改變定位屬性在固定時(shí)間內(nèi)執(zhí)行一次
3、 給fixedElement開(kāi)啟硬件加速
基本邏輯如下圖:
測(cè)試頁(yè)面二維碼:
position:sticky是個(gè)什么鬼?對(duì)與css的position屬性我們只知道有static、relative、absolute、fixed這四個(gè)值,什么時(shí)候又多出了sticky這個(gè)值。看下MDN文檔解釋
Sticky positioning
Sticky positioning is a hybrid of relative and fixed positioning. The element is treated as relative positioned until it crosses a specified threshold, at which point it is treated as fixed positioned.
大概意思是:sticky定位時(shí)relative定位與fixed定位的混合體。對(duì)于設(shè)置了sticky定位的元素,在它的頂部到達(dá)一個(gè)指定的界限之前會(huì)被當(dāng)作relative定位,超過(guò)這個(gè)界限字后則被當(dāng)作fixed定位。這個(gè)界限就是 該元素頂部距離窗口頂部的距離等于該元素設(shè)置的top值
比如以下demo:
60像素分割線當(dāng)我的頂部距離窗口頂部為10px(top值)時(shí),我就會(huì)像fixed一樣fixed在距離窗口10px(top值處)sticky的co時(shí)代發(fā)送分ntent
.top{height:60px;background:#f20;width:100%;color: #fff;font-size:16px;text-align:center;line-height:60px;} .sticky{position:sticky;position:-webkit-sticky;top:10px;height:40px;background:#dd5;color:#fff;line-height:20px;text-align:center;} .sticky-t10{top:0px;} .content{height:1000px;width:100%;background:#f8f8f8;text-align:center;padding-top:40px;color:#333;}
效果圖:
當(dāng)頁(yè)面滾動(dòng)到距離黃色區(qū)塊頂部10px時(shí),黃色區(qū)塊就會(huì)fixed在窗口頂部10px處,頁(yè)面再往下滾動(dòng)距離也不會(huì)變。當(dāng)頁(yè)面網(wǎng)上滾動(dòng)時(shí),頁(yè)面頂部距離黃色區(qū)塊頂部大于10px時(shí),黃色區(qū)塊又會(huì)恢復(fù)原樣固定在原來(lái)的位置。
position:sticky這個(gè)屬性并不會(huì)出現(xiàn)當(dāng)頁(yè)面滾動(dòng)停止之后才會(huì)出現(xiàn)的bug,因?yàn)樗旧砭褪菍儆谡A鳌2⒉粫?huì)像fixed 與static相互切換時(shí)引起重排于重繪,而移動(dòng)端瀏覽器滾動(dòng)時(shí)是禁止重排跟重繪的,所以才會(huì)導(dǎo)致以上出現(xiàn)的問(wèn)題。下圖是對(duì)于position:sticky的支持情況:
發(fā)現(xiàn)支持的瀏覽器一般般,但是經(jīng)過(guò)測(cè)試像微信、safari、uc等瀏覽器是支持的,雖然chrome不支持,但是在chrome使用優(yōu)化后的fixed定位也可以解決這個(gè)問(wèn)題,基本能滿足主流的瀏覽器就夠了,其他的見(jiàn)鬼去吧。
滾動(dòng)時(shí)減少性能損耗,強(qiáng)制觸發(fā)瀏覽器的同步布局如果瀏覽器不支持position:sticky,那么就使用js動(dòng)態(tài)的在節(jié)點(diǎn)在fixed定位于static定位中切換,但是需要對(duì)切換過(guò)程做一些優(yōu)化。
1、使用函數(shù)節(jié)流防抖減少dom操作頻繁粗發(fā),但是保證在規(guī)定時(shí)間內(nèi)必須執(zhí)行一次。
2、使用window.requestAnimationFrame 方法在下一幀前觸發(fā)瀏覽器的強(qiáng)制同步布局,是對(duì)dom的操作能及時(shí)渲染到頁(yè)面上。
3、減少對(duì)dom的讀寫操作,或者把dom操作把讀、寫操作分開(kāi),可以減少渲染次數(shù)。
由于移動(dòng)設(shè)備的硬件限制,導(dǎo)致移動(dòng)端的瀏覽器的渲染能比較差。此時(shí)對(duì)需要定位的元素開(kāi)啟硬件加速,會(huì)把需要渲染的元素放到特定的復(fù)合層『Composited Layer』中,當(dāng)該元素改變時(shí)可以較少重繪或重排的范圍。給元素添加 transform: translateZ(0);屬性就行。
參考:硬件加速:
http://div.io/topic/1348
http://www.cnblogs.com/shyton...
提升頁(yè)面性能:
https://developer.mozilla.org...
http://www.ruanyifeng.com/blo...
http://www.jianshu.com/p/a32b...
具體實(shí)現(xiàn)請(qǐng)參考以下jquery版本的代碼:
//jquery (function() { function Sticky(){ this.init.apply(this, arguments); } /** * 滾動(dòng)fixed組件初始化 * @param {object} setting allocate傳進(jìn)來(lái)的參數(shù) * @param {object} setting.stickyNode 需要設(shè)置position:sticky的節(jié)點(diǎn),通常是最外層 * @param {object} setting.fixedNode 當(dāng)滾動(dòng)一定距離時(shí)需要fixed在頂部的節(jié)點(diǎn) * @param {int} setting.top fixed之后距離頂部的top值 * @param {int} setting.zIndex fixed之后的z-index值 * @param {string} setting.fixedClazz fixed時(shí)給fixedNode添加的類 * @param {function} setting.runInScrollFn 滾動(dòng)期間額外執(zhí)行的函數(shù) * @return {void} */ Sticky.setting = { stickyNode: null, fixedNode: null, top: 0, zIndex: 100, fixedClazz: "", runInScrollFn: null }; var sPro = Sticky.prototype; var g = window; /** * 初始化 * @param {object} options 設(shè)置 * @return {void} */ sPro.init = function(options){ this.setting = $.extend({}, Sticky.setting, options, true); if (options.fixedNode) { this.fixedNode = options.fixedNode[0] || options.fixedNode; this.stickyNode = options.stickyNode[0] || options.stickyNode; this.cssStickySupport = this.checkStickySupport(); this.stickyNodeHeight = this.stickyNode.clientHeight; this.fixedClazz = options.fixedClazz; this.top = parseInt(options.top, 10) || 0; this.zIndex = parseInt(options.zIndex) || 1; this.setStickyCss(); this.isfixed = false; // 把改變定位的操作添加到節(jié)流函數(shù)與window.requestAnimationFrame方法中,確保一定事件內(nèi)必須執(zhí)行一次 this.onscrollCb = this.throttle(function() { this.nextFrame(this.sticky.bind(this)); }.bind(this), 50, 100); this.initCss = this.getInitCss(); this.fixedCss = this.getFixedCss(); this.addEvent(); } }; /** * 獲取原始css樣式 * @return {string} 定位的樣式 */ sPro.getInitCss = function() { if (!!this.fixedNode) { return "position:" + this.fixedNode.style.position + ";top:" + this.fixedNode.style.top + "px;z-index:" + this.fixedNode.style.zIndex + ";"; } return ""; }; /** * 生成fixed時(shí)的css樣式 * @return {void} */ sPro.getFixedCss = function() { return "position:fixed;top:" + this.top + "px;z-index:" + this.zIndex + ";"; }; /** * 給fixedNode設(shè)置fixed定位樣式 * @param {string} style fixed定位的樣式字符串 */ sPro.setFixedCss = function(style) { if(!this.cssStickySupport){ if (!!this.fixedNode){ this.fixedNode.style.cssText = style; } } }; /** * 檢查瀏覽器是否支持positon: sticky定位 * @return {boolean} true 支持 false 不支持 */ sPro.checkStickySupport = function() { var div= null; if(g.CSS && g.CSS.supports){ return g.CSS.supports("(position: sticky) or (position: -webkit-sticky)"); } div = document.createElement("div"); div.style.position = "sticky"; if("sticky" === div.style.position){ return true; } div.style.position = "-webkit-sticky"; if("-webkit-sticky" === div.style.position){ return true; } div = null; return false; }; /** * 給sticyNode設(shè)置position: sticky定位 */ sPro.setStickyCss = function() { if(this.cssStickySupport){ this.stickyNode.style.cssText = "position:-webkit-sticky;position:sticky;top:" + this.top + "px;z-index:" + this.zIndex + ";"; } }; /** * 監(jiān)聽(tīng)window的滾動(dòng)事件 */ sPro.addEvent = function() { $(g).on("scroll", this.onscrollCb.bind(this)); }; /** * 讓函數(shù)在規(guī)定時(shí)間內(nèi)必須執(zhí)行一次 * @param {Function} fn 定時(shí)執(zhí)行的函數(shù) * @param {int} delay 延遲多少毫秒執(zhí)行 * @param {[type]} mustRunDelay 多少毫秒內(nèi)必須執(zhí)行一次 * @return {[type]} [description] */ sPro.throttle = function(fn, delay, mustRunDelay){ var timer = null; var lastTime; return function(){ var now = +new Date(); var args = arguments; g.clearTimeout(timer); if(!lastTime){ lastTime = now; } if(now - lastTime > mustRunDelay){ fn.apply(this, args); lastTime = now; }else{ g.setTimeout(function(){ fn.apply(this, args); }.bind(this), delay); } }.bind(this); }; /** * window.requestAnimationFrame的兼容性寫法,保證在100/6ms執(zhí)行一次 * @param {Function} fn 100/16ms需要執(zhí)行的函數(shù) * @return {void} */ sPro.nextFrame = (function(fn){ var prefix = ["ms", "moz", "webkit", "o"]; var handle = {}; handle.requestAnimationFrame = window.requestAnimationFrame; for(var i = 0; i < prefix.length && !handle.requestAnimationFrame; ++i){ handle.requestAnimationFrame = window[prefix[i] + "RequestAnimationFrame"]; } if(!handle.requestAnimationFrame){ handle.requestAnimationFrame = function(fn) { var raf = window.setTimeout(function() { fn(); }, 16); return raf; }; } return function(fn) { handle.requestAnimationFrame.apply(g, arguments); } })(); /** * 判斷stickyNode的當(dāng)前位置設(shè)置fixed|static|sticky定位 * @return {void} */ sPro.sticky = function() { this.setting.runInScrollFn && this.setting.runInScrollFn(); var stickyNodeBox = this.stickyNode.getBoundingClientRect(); if(stickyNodeBox.top <= this.top && !this.isfixed){ this.setFixedCss(this.fixedCss); this.fixedClazz && $(this.fixedNode).addClass(this.fixedClazz); this.isfixed = true; $(this).trigger("onsticky", true); } else if(stickyNodeBox.top > this.top && this.isfixed) { this.setFixedCss(this.initCss.replace(/position:[^;]*/, "position:static")); g.setTimeout(function() { this.setFixedCss(this.initCss) }.bind(this), 30); this.fixedClazz && $(this.fixedNode).removeClass(this.fixedClazz); this.isfixed = false; $(this).trigger("onsticky", true); } }; $.initSticky = function(options){ return new Sticky(options); }; })();
html 結(jié)構(gòu)
css 結(jié)構(gòu)
.g-page-box .m-nav?{ height:?1.33333rem; } .g-page-box .m-nav .nav-fixed?{ height:?.86667rem; padding:?.22667rem .50667rem; background-color:?#1aadbb; position:?relative; transform:?translate3d(0, 0, 0); -webkit-transform:?translate3d(0, 0, 0); transition:?height 4s; } .fixed{ position:?fixed; top:?0px; z-index:?100; }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/115453.html
摘要:這個(gè)界限就是該元素頂部距離窗口頂部的距離等于該元素設(shè)置的值比如以下像素分割線當(dāng)我的頂部距離窗口頂部為值時(shí),我就會(huì)像一樣在距離窗口值處的時(shí)代發(fā)送分效果圖當(dāng)頁(yè)面滾動(dòng)到距離黃色區(qū)塊頂部時(shí),黃色區(qū)塊就會(huì)在窗口頂部處,頁(yè)面再往下滾動(dòng)距離也不會(huì)變。 position: sticky; fixed 吸頂 頁(yè)面滾動(dòng)結(jié)束后頁(yè)面才渲染 需求 經(jīng)常會(huì)有這樣的需求,當(dāng)頁(yè)面滾動(dòng)到某一個(gè)位置fixedTopV...
摘要:這個(gè)界限就是該元素頂部距離窗口頂部的距離等于該元素設(shè)置的值比如以下像素分割線當(dāng)我的頂部距離窗口頂部為值時(shí),我就會(huì)像一樣在距離窗口值處的時(shí)代發(fā)送分效果圖當(dāng)頁(yè)面滾動(dòng)到距離黃色區(qū)塊頂部時(shí),黃色區(qū)塊就會(huì)在窗口頂部處,頁(yè)面再往下滾動(dòng)距離也不會(huì)變。 position: sticky; fixed 吸頂 頁(yè)面滾動(dòng)結(jié)束后頁(yè)面才渲染 需求 經(jīng)常會(huì)有這樣的需求,當(dāng)頁(yè)面滾動(dòng)到某一個(gè)位置fixedTopV...
摘要:用于獲得當(dāng)前元素到定位父級(jí)頂部的距離偏移值。后來(lái)在項(xiàng)目中總會(huì)遇到滾動(dòng)吸頂?shù)男Ч枰獙?shí)現(xiàn),現(xiàn)在我將我知道的種滾動(dòng)吸頂實(shí)現(xiàn)方式做詳細(xì)介紹。有兼容性問(wèn)題,在微信瀏覽器某些版本中的值會(huì)為,于是乎也就有了第三種方案的兼容性寫法。修改版預(yù)覽 這篇文章是三天前寫就的,有大佬給我提了一些修改意見(jiàn),我覺(jué)得這個(gè)意見(jiàn)確實(shí)中肯。所以就有了這個(gè)升級(jí)的修改版本。代碼同步更新到 GitHub 了。 修改內(nèi)容如下: 添加...
摘要:因?yàn)轫?xiàng)目需求,最近開(kāi)始轉(zhuǎn)到微信公眾號(hào)開(kāi)發(fā),接觸到了框架,這個(gè)效果的實(shí)現(xiàn)雖說(shuō)是基于框架下實(shí)現(xiàn)的,但是同樣也可以借鑒到其他地方,原理都是一樣的。上面我們得到了一個(gè)的屬性值,接下來(lái)我們只需要根據(jù)它的值來(lái)設(shè)置吸頂元素的屬性就可以了。 因?yàn)轫?xiàng)目需求,最近開(kāi)始轉(zhuǎn)到微信公眾號(hào)開(kāi)發(fā),接觸到了Vue框架,這個(gè)效果的實(shí)現(xiàn)雖說(shuō)是基于Vue框架下實(shí)現(xiàn)的,但是同樣也可以借鑒到其他地方,原理都是一樣的。 進(jìn)入正題...
閱讀 675·2021-09-30 09:47
閱讀 2868·2021-09-04 16:40
閱讀 852·2019-08-30 13:18
閱讀 3447·2019-08-29 16:22
閱讀 1550·2019-08-29 12:36
閱讀 583·2019-08-29 11:11
閱讀 1473·2019-08-26 13:47
閱讀 1126·2019-08-26 13:32