css实现5种滚动吸顶实现方式的比较(性能升级版)

css实现5种滚动吸顶实现方式的比较(性能升级版)

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

// some code

// 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 值来实现吸顶效果,即:

使⽤ `ndingClientRect().top` 实现
这个⽅案就可以解决抖动的 Bug 了。⼆、吸顶效果不能及时响应这个问题是我⽐较头痛,之前我没有在意过这个问题。直到有⼀天我⽤美团点外卖的时候,我才开始注意这个问题。描述:1. 当页⾯往下滚动时,吸顶元素需要等页⾯滚动停⽌之后才会出现吸顶效果2. 当页⾯往上滚动时,滚动到吸顶元素恢复⽂档流位置时吸顶元素不恢复原样,⽽等页⾯停⽌滚动之后才会恢复原样原因:在 ios 系统上不能实时监听 scroll 滚动监听事件,在滚动停⽌时才触发其相关的事件。解决⽅案:还记得第⼀种⽅案中的 position:sticky 吗?这个属性在 IOS6 以上的系统中有良好的兼容性,所以我们可以区分 IOS 和Android 设备做两种处理。IOS 使⽤ position:sticky,Android 使⽤滚动监听 getBoundingClientRect().top 的值。如果 IOS 版本过低呢?这⾥提供⼀种思路:tAnimationFrame()。性能优化篇(新增)到此 4 中滚动吸顶的⽅式介绍完了,可是这样就真的结束了吗?其实还是有优化的空间的。我们从两个⽅向做性能优化(其实是⼀个⽅向):1. 避免过度的 reflow2. 优化滚动监听事件问题定位过程我们知道过度的 reflow 会使页⾯的性能下降。所以我们需要尽可能的降低 reflow 的次数,给⽤户更加流畅的感觉。有的朋友或许会说这个我知道,可是这和滚动吸顶有什么关系呢?不急,你是否还记得滚动吸顶使⽤了 offsetTop 或者 getBoundingClientRect().top 来获取响应的偏移量呢?既然有读取元素的属性就⾃然会导致页⾯ reflow。因此我们优化的⽅向就是从减少读取元素属性次数下⼿,查看代码发现⼀触发屏幕滚动事件就会调⽤相关⽅法读取元素的偏移量。优化⽅案解决这个问题有以下两个⽅案:1. 牺牲平滑度满⾜性能,使⽤节流控制相关⽅法的调⽤2. 使⽤ IntersectionObserver 和节流结合,也牺牲了平滑度。第⼀种⽅案这个⽅案很常见,不过其带来的副作⽤也很明显,就是在吸顶效果会有些延迟,如果产品可以接受那就不失为⼀种好⽅法。这样可以控制在⼀定时间内只读取这⾥节流函数就直接是⽤ 封装好的 throttle ⽅法。代码如下:ntListener('scroll', _.throttle(ScrollThree, 50));第⼆种⽅案第⼆种⽅案相对来说容易接受⼀点,就是⽀持 IntersectionObserver 就⽤ IntersectionObserver,否则就⽤ throttle。我们先讲讲 IntersectionObserverIntersectionObserver 可以⽤来监听元素是否进⼊了设备的可视区域之内,⽽不需要频繁的计算来做这个判断。通过这个属性我们就可以在元素不在可视范围内,不去读取元素的相对位置,已达到性能优化;当浏览器不⽀持这个属性的时候就使⽤ throttle 来处理。我们看看这个属性的兼容性怎么样:⼤概⽀持了 60% 以上,在项⽬中还是可以使⽤的(你需要做好兼容性处理)。关于 IntersectionObserver 如何使⽤,请看或者。使⽤ IntersectionObserver 和 throttle 优化的代码如下:IntersectionObserverFun: function() { let self = this; let ele = self.$_tab_fixed; if( !IntersectionObserver ){ let observer = new IntersectionObserver(function(){ let offsetTop = ndingClientRect().top; ixed = offsetTop < 0; }, { threshold: [1] }); e(mentsByClassName('title_box')[0]); } else { ntListener('scroll', _.throttle(function(){ let offsetTop = ndingClientRect().top; ixed = offsetTop < 0; }, 50)); }},

注意IntersectionObserver API 是异步的,不随着⽬标元素的滚动同步触发。规格写明,IntersectionObserver的实现,应该采⽤ requestIdleCallback()。它不会⽴即执⾏回调,它会调⽤tIdleCallback() 来异步的执⾏我们指定的回调函数,⽽且还规定了最⼤的延迟时间是 100 毫秒。总结:这种 IntersectionObserver 和 throttle 结合的⽅案不失为⼀种可选择的⽅案,这种⽅案的优点就在于可以有效地减少页⾯reflow 的风险,不过缺点也是有的,需要牺牲页⾯的平滑度。具体该如何取舍,就看业务的需要啦。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

发布者:admin,转转请注明出处:http://www.yc00.com/web/1690560947a369148.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信