滑动吸顶效果的常见实现方式有以下几种:
1.positon: sticky
2.监听元素的滚动事件,使用offset判断
3.obj.getBoundingClientRect().top
下面依次介绍
postion: sticky
属性值是css3新加入的属性,近似于relative和fixed的结合体
sticky被称为粘性定位元素,postion属性值为sticky的元素在目标区域内时表现的和position: relative
时无异。
当元素满足粘性定位的要求时(如top: 100px
),他的表现与position: fixed
无异
元素固定的相对偏移是相对于离它最近的具有滚动框的祖先元素,如果祖先元素都不可以滚动,那么是相对于viewport来计算元素的偏移量。
使用条件:
overflow:hidden/auto
坑:
兼容性:
兼容性不太友善,ios虽然支持度还行但是刘海屏的表现暂时待定
使用方式:
.sticky{ positon: sticky; top: 10px } 复制代码
首先来复习以下offset值的含义:
距离拥有相对定位的父级元素的顶部偏移量
那么offset并不一定表示元素距离页面顶部的距离,也可能是与拥有相对定位的父级元素的顶部距离
假设我们需要的吸顶效果是在body的顶部,我们可以如下改造一个方法
getOffset(obj,direction) { let offsetL = 0; let offsetT = 0; // 依次获取父级元素的offsetLeft/offsetTop while( obj!== window.document.body && obj !== null ){ offsetL += obj.offsetLeft; offsetT += obj.offsetTop; obj = obj.offsetParent; } if(direction === 'left'){ return offsetL; }else { return offsetT; } } handleScroll(e) { let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; let offsetTop = getOffset(e.target,'top'); if(scrollTop > offsetTop){ // fixed } } 复制代码
优势:兼容性优秀
劣势:存在性能问题,最好搭配节流函数使用
定义:返回某个元素相对浏览器视窗上下左右的距离
也就是说,这个api完全可以取代上面那个函数...
兼容性:
优势:简 洁 , 兼 容 性 极 佳
劣势:没有解决reflow过多的性能问题
众所周知,所有的scroll问题都逃不过高强度reflow带来的性能压力
解决方法也非常明确——牺牲平滑度,减少触发次数
在通常情况下,我们可以直接使用节流函数直接来限制触发次数,比如这么做
// 假设你封装好了一个throttle(func,time)的节流函数 window.addEventListener('scroll', throttle(handleScroll, 20)); 复制代码
但这么使用有一个问题。。那就是会出现吸顶时有一定卡顿的情况,就不够润。
那咋办呢?
我们不妨换种思路。。。不如精确控制handleScroll()函数的触发时机?
自然我们就想到了 IntersectionObserver API
IntersectionObserver API 的使用教程请移步这里
这个api的主要目的是用来判断一个元素是否在可视范围内(并可以控制触发时机)
兼容性:
不难发现还是有那么一点兼容性问题的。所以我们不如将throttle和IntersectionObserver 方案结合起来使用
this.flag == false scrollFunc(e) { if( IntersectionObserver ){ const observer = new IntersectionObserver(function(){ const offsetTop = e.target.getBoundingClientRect().top; if(offsetTop < 0){ // 吸顶 this.flag == true }else{ this.flag == false } }, { // 100%时触发回调函数 threshold: [1] }); observer.observe(e.target); } else { window.addEventListener('scroll', throttle(()=>{ let offsetTop = e.target.getBoundingClientRect().top; if(offsetTop < 0){ // 吸顶 this.flag == true }else{ this.flag == false } }, 20)); } } 复制代码