当前位置: 首页 > Web前端 > HTML

解决页面滚动时吸顶操作不能及时响应bug

时间:2023-04-02 21:23:03 HTML

解决页面滚动时吸顶操作无法及时响应的BUG。position:sticky;fixed天花板页面滚动完成后才渲染页面。经常有这样的需求。当页面滚动到某个位置fixedTopValue时,需要将某个元素fixedElement固定在屏幕顶部。基本方法是获取页面的scrollTop值进行判断:ifscrollTop>fixedTopValue;然后添加position:fixed;top:0;否则删除position:fixed;属性。在PC浏览器中操作是正常的。真机测试时总会出现奇怪的现象。例如:1、当页面向下滚动时,直到页面滚动停止,fixedElement才会出现。2、向上滚动时,出现在固定位置时不会返回原位,而是在页面停止滚动后返回原位。3.滚动到顶部后,会出现两个一模一样的fixedElement,过一会又会恢复正常。这样的用户体验实在是太差了,所以迫切需要解决这个问题。解决方案主要涉及以下三个方面1.使用新的定位属性position:sticky;(如果支持的话)2.如果1不支持,使用window.requestAnimationFrame方法保证定位属性的改变在固定时间内执行一次3.启用fixedElement硬件加速的基本逻辑如下:二维码测试页上:position:sticky是什么鬼?对于css的position属性,我们只知道有四个值:static、relative、absolute、fixed。什么时候会有额外的粘性值。查看解释粘性定位的MDN文档粘性定位是相对定位和固定定位的混合体。该元素被视为相对定位,直到它超过指定的阈值,此时它被视为固定定位。大概意思是:当sticky定位时是relative定位和fixed定位的混合体。对于设置了粘性定位的元素,直到它的top到达一个指定的limit,才会被认为是相对定位,超过这个limitword,就会被认为是固定定位。此限制是从元素顶部到窗口顶部的距离等于元素设置的顶部值。例如下面的demo:

60像素分割线
当我的top距离窗口顶部10px(top值)时,我会像fixed固定在距离窗口10px(top值)
sticky的co-timessendcontent
.top{height:60px;背景:#f20;宽度:100%;颜色:#fff;字体大小:16px;文本对齐:中心;行高:60px;}.sticky{位置:粘性;位置:-webkit-粘性;顶部:10px;高度:40px;背景:#dd5;颜色:#fff;行高:20px;文本对齐:居中;}.sticky-t10{top:0px;}.content{height:1000px;width:100%;background:#f8f8f8;text-align:center;padding-top:40px;color:#333;}效果图:当页面滚动到黄色块顶部10px时,黄色块会固定在10px从窗口顶部开始,页面的滚动距离不会改变。当页面在线滚动时,当页面顶部距离黄色块顶部大于10px时,黄色块会回到原来的位置,固定在原来的位置。position:sticky属性不会出现页面滚动停止后才出现的bug,因为它本身属于正常流程。在fixed和static之间切换时不会造成重排和重绘,而移动端浏览器禁止滚动时重排和重绘,所以会造成上述问题。下图是对position:sticky的支持:支持的浏览器一般,但经过测试,微信、safari、uc等浏览器都支持。虽然chrome不支持,但是使用chrome中优化的固定定位也是可以的。这个问题可以解决,基本满足主流浏览器就可以了,其他的去死吧。减少滚动时的性能损失,强制浏览器同步布局。如果浏览器不支持position:sticky,则使用js动态切换节点的固定位置和静态位置,但需要对切换过程做一些优化。1、使用函数节流和防抖,减少频繁粗暴的dom操作,但保证在规定时间内必须执行一次。2、使用window.requestAnimationFrame方法在下一帧前触发浏览器强制同步布局,使对dom的操作及时渲染到页面上。3、减少对dom的读写操作,或者将dom操作和读写操作分开,可以减少渲染次数。为需要定位的元素启用硬件加速由于移动设备的硬件限制,移动浏览器的渲染性能比较差。此时需要对需要定位的元素启用硬件加速,将需要渲染的元素放置在特定的复合层“CompositedLayer”中。当元素发生变化时,可以减少重新绘制或重新排列的范围。只需添加transform:translateZ(0);元素的属性。参考:硬件加速:http://div.io/topic/1348http://www.cnblogs.com/shyton...提升页面性能:https://developer.mozilla.org...http://www.ruanyifeng.com/blo...http://www.jianshu.com/p/a32b...具体实现参考如下jquery版本代码://jquery(function(){functionSticky(){this.init.apply(this,arguments);}/***滚动固定组件初始化*@param{object}设置分配传入的参数*@param{object}setting.stickyNode需要设置位置的节点:sticky,通常是最外层*@param{object}setting.fixedNode滚动一定距离需要固定在最上面的节点*@param{int}setting.topfixed从最上面开始的值*@param{int}setting.zIndex固定z-index值*@param{string}setting.fixedClazzfixed时添加到fixedNode的类*@param{function}setting.runInScrollFn滚动时执行的附加函数*@return{void}*/Sticky.setting={stickyNode:null,fixedNode:null,top:0,zIndex:100,fixedClazz:'',runInScrollFn:null};varsPro=Sticky.prototype;变量g=窗口;/***初始化*@param{object}选项设置*@return{void}*/sPro.init=function(options){this.setting=$.extend({},Sticky.setting,options,true);if(options.fixedNode){this.fixedNode=options.fixedNode[0]||选项.固定节点;this.stickyNode=选项。粘性节点[0]||选项.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;这个.setStickyCss();this.isfixed=false;//在节流函数和window.requestAnimationFrame方法中加入定位改变操作,保证在某个事件内必须执行一次this.onscrollCb=this.throttle(function(){this.nextFrame(this.sticky.bind(this));}.bind(this),50,100);this.initCss=this.getInitCss();this.fixedCss=this.getFixedCss();这个.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+";";}返回””;};/***固定时生成css样式*@return{void}*/sPro.getFixedCss=function(){return"position:fixed;top:"+this.top+"px;z-index:"+this.zIndex+";";};/***为fixedNode设置固定定位样式*@param{string}style固定定位样式字符串*/sPro.setFixedCss=function(style){if(!this.cssStickySupport){if(!!this.fixedNode){this.fixedNode.style.cssText=样式;}}};/***检查浏览器是否支持positionon:sticky定位*@return{boolean}true支持false不支持*/sPro.checkStickySupport=function(){vardiv=null;if(g.CSS&&g.CSS.supports){returng.CSS.supports("(position:sticky)or(position:-webkit-sticky)");}div=document.createElement("div");div.style.position="粘性";if("sticky"===div.style.position){返回true;}div.style.position="-webkit-sticky";if("-webkit-sticky"===div.style.position){返回true;}div=null;返回假;};/***设置stickyNode的位置:粘性定位*/sPro.setStickyCss=function(){if(this.cssStickySupport){this.stickyNode.style.cssText="position:-webkit-sticky;position:sticky;top:"+this.top+"px;z-index:"+this.zIndex+";";}};/***监听窗口滚动事件*/sPro.添加事件=function(){$(g).on('滚动',this.onscrollCb.bind(this));};/***使函数必须在指定时间内执行一次*@param{Function}fn定时执行函数*@param{int}delay延迟多少毫秒执行*@param{[type]}mustRunDelay多少毫秒必须执行一次*@return{[type]}[description]*/sPro.throttle=function(fn,delay,mustRunDelay){vartimer=null;上次变种;返回函数(){varnow=+newDate();var参数=参数;g.clearTimeout(计时器);如果(!lastTime){lastTime=now;}if(now-lastTime>mustRunDelay){fn.apply(this,args);最后一次=现在;}else{g.setTimeout(function(){fn.apply(this,args);}.bind(this),delay);}}.bind(这个);};/***兼容window.requestAnimationFrame的写法,保证100/6ms执行一次*@param{Function}fn100/16ms需要执行的函数*@return{void}*/sPro.nextFrame=(function(fn){v??arprefix=["ms","moz","webkit","o"];varhandle={};handle.requestAnimationFrame=window.requestAnimationFrame;for(vari=0;ithis.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){returnnewSticky(options);};})();html结构懂少儿编程参与公益直播课上传编程作品
css结构.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);过渡:高度4s;}.fixed{position:fixed;top:0px;z-index:100;}