2023年7月29日发(作者:)
css实现5种滚动吸顶实现⽅式的⽐较(性能升级版)修改版预览这篇⽂章是三天前写就的,有⼤佬给我提了⼀些修改意见,我觉得这个意见确实中肯。所以就有了这个升级的修改版本。代码同步更新到 了。修改内容如下:1. 添加了图⽂说明,直观的说明 getBoundingClientRect()集合含义2. 频繁 reflow 风险该如何规避(优化滚动监听)3. 监听滚动带来的性能问题(使⽤ IntersectionObserver, 新⽅案)修改更新的内容在第 4 点和第 5 点,如果你看过本⽂,可以直接看修改更新的内容。或者你可以再看⼀遍。前⾔我⼊职第⼆家公司接到的第⼀个需求就是修复之前外包做的滚动吸顶效果。我当时很纳闷为何⼀个滚动吸顶会有 bug,后来我查看代码才发现直接⽤的是 offsetTop 这个属性,⽽且并没有做兼容性处理。offsetTop⽤于获得当前元素到定位⽗级( Parent )顶部的距离(偏移值)。定位⽗级 offsetParent 的定义是:与当前元素最近的 position != static 的⽗级元素。或许写这个代码的⼈没有注意到“定位⽗级”这个这个附属条件。后来在项⽬中总会遇到滚动吸顶的效果需要实现,现在我将我知道的 4 种滚动吸顶实现⽅式做详细介绍。以上这四种⽅式你都了解吗?相关代码已上传到 ,感兴趣的可以 clone 代码到本地运⾏。望给个 star ⽀持⼀下。四种实现⽅式我们先看⼀下效果图:⼀、使⽤ position:sticky 实现1、粘性定位是什么?粘性定位 sticky 相当于相对定位 relative 和固定定位 fixed 的结合;在页⾯元素滚动过程中,某个元素距离其⽗元素的距离达到 sticky 粘性定位的要求时;元素的相对定位 relative 效果变成固定定位 fixed 的效果。2、如何使⽤?使⽤条件:1. ⽗元素不能 overflow:hidden 或者 overflow:auto 属性2. 必须指定 top、bottom、left、right 4 个值之⼀,否则只会处于相对定位3. ⽗元素的⾼度不能低于 sticky 元素的⾼度4. sticky 元素仅在其⽗元素内⽣效在需要滚动吸顶的元素加上以下样式便可以实现这个效果:.sticky { position: -webkit-sticky; position: sticky; top: 0;}3、这个属性好⽤吗?我们先看下在 Can I use 中看看这个属性的兼容性:可以看出这个属性的兼容性并不是很好,因为这个 API 还只是实验性的属性。不过这个 API 在 IOS 系统的兼容性还是⽐较好的。所以我们在⽣产环境如果使⽤这个 API 的时候⼀般会和下⾯的⼏种⽅式结合使⽤。⼆、使⽤ JQuery 的 offset().top 实现我们知道 JQuery 中封装了操作 DOM 和读取 DOM 计算属性的 API,基于 offset().top 这个 API 和 scrollTop() 的结合,我们也可以实现滚动吸顶效果。...ntListener('scroll', ScrollOne);...handleScrollOne: function() { let self = this; let scrollTop = $('html').scrollTop(); let offsetTop = $('.title_box').offset().top; ixed = scrollTop > offsetTop;}...这样实现固然可以,不过由于 JQuery 慢慢的退出历史的舞台,我们在代码中尽量不使⽤ JQuery 的 API。我们可以基于offset().top 的源码⾃⼰处理原⽣ offsetTop。于是乎就有了第三种⽅式。scrolloTop() 有兼容性问题,在微信浏览器、IE、某些 firefox 版本中 $('html').scrollTop() 的值会为 0,于是乎也就有了第三种⽅案的兼容性写法。三、使⽤原⽣的 offsetTop 实现我们知道 offsetTop 是相对定位⽗级的偏移量,倘若需要滚动吸顶的元素出现定位⽗级元素,那么 offsetTop 获取的就不是元素距离页⾯顶部的距离。我们可以⾃⼰对 offsetTop 做以下处理:getOffset: function(obj,direction){ let offsetL = 0; let offsetT = 0; while( obj!== && obj !== null ){ offsetL += Left; offsetT += Top; obj = Parent; } if(direction === 'left'){ return offsetL; }else { return offsetT; }}使⽤:...ntListener('scroll', ScrollTwo);...handleScrollTwo: function() { let self = this; let scrollTop = ffset || Top || Top; let offsetTop = set(self.$_tab_fixed); ixed = scrollTop > offsetTop;}...你是不是看出了以上两种⽅式的⼀些问题?我们⼀定需要使⽤ scrollTop - offsetTop 的值来实现滚动吸顶的效果吗?答案是否定的。我们⼀同看看第四种⽅案。四、使⽤ ndingClientRect().top 实现定义:这个 API 可以告诉你页⾯中某个元素相对浏览器视窗上下左右的距离。使⽤:tab 吸顶可以使⽤ ndingClientRect().top 代替 scrollTop - offsetTop,代码如下:// html
// vueexport default { data(){ return{ titleFixed: false } }, activated(){ ixed = false; ntListener('scroll', Scroll); }, methods: { //滚动监听,头部固定 handleScroll: function () { let offsetTop = this.$_tab_ndingClientRect().top; ixed = offsetTop < 0; // some code } } }offsetTop 和 getBoundingClientRect() 区别1. getBoundingClientRect():⽤于获得页⾯中某个元素的左,上,右和下分别相对浏览器视窗的位置。不包含⽂档卷起来的部分。该函数返回⼀个 object 对象,有8个属性:top, right, buttom, left, width, height, x, y2. offsetTop:⽤于获得当前元素到定位⽗级( Parent )顶部的距离(偏移值)。定位⽗级 offsetParent 的定义是:与当前元素最近的 position != static 的⽗级元素。offsetTop 和 offsetParent ⽅法相结合可以获得该元素到 body 上边距的距离。代码如下:getOffset: function(obj,direction){ let offsetL = 0; let offsetT = 0; while( obj!== && obj !== null ){ offsetL += Left; offsetT += Top; obj = Parent; } if(direction === 'left'){ return offsetL; }else { return offsetT; }}延伸知识点offsetWidth:元素在⽔平⽅向上占⽤的空间⼤⼩:offsetWidth = border-left + padding-left + width + padding-right + border-rightoffsetHeight:元素在垂直⽅向上占⽤的空间⼤⼩:offsetHeight = border-top + padding-top + height + padding-bottom + border-bottom注:如果存在垂直滚动条,offsetWidth 也包括垂直滚动条的宽度;如果存在⽔平滚动条,offsetHeight 也包括⽔平滚动条的⾼度;offsetTop:元素的上外边框⾄ offsetParent 元素的上内边框之间的像素距离;offsetLeft:元素的左外边框⾄ offsetParent 元素的左内边框之间的像素距离;注意事项1. 所有偏移量属性都是只读的;2. 如果给元素设置了 display:none,则它的偏移量属性都为 0;3. 每次访问偏移量属性都需要重新计算(保存变量);4. 在使⽤的时候可能出现 DOM 没有初始化,就读取了该属性,这个时候会返回 0;对于这个问题我们需要等到 DOM 元素初始化完成后再执⾏。遇到的两个问题⼀、吸顶的那⼀刻伴随抖动出现抖动的原因是因为:在吸顶元素 position 变为 fixed 的时候,该元素就脱离了⽂档流,下⼀个元素就进⾏了补位。就是这个补位操作造成了抖动。解决⽅案为这个吸顶元素添加⼀个等⾼的⽗元素,我们监听这个⽗元素的 getBoundingClientRect().top 值来实现吸顶效果,即:
注意IntersectionObserver API 是异步的,不随着⽬标元素的滚动同步触发。规格写明,IntersectionObserver的实现,应该采⽤ requestIdleCallback()。它不会⽴即执⾏回调,它会调⽤tIdleCallback() 来异步的执⾏我们指定的回调函数,⽽且还规定了最⼤的延迟时间是 100 毫秒。总结:这种 IntersectionObserver 和 throttle 结合的⽅案不失为⼀种可选择的⽅案,这种⽅案的优点就在于可以有效地减少页⾯reflow 的风险,不过缺点也是有的,需要牺牲页⾯的平滑度。具体该如何取舍,就看业务的需要啦。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1690560947a369148.html
评论列表(0条)