背景上一篇:记一个“InfiniteList”滚动优化,介绍了“如何优化一个无限滚动列表”。使用懒加载方案时,一个关键点是:需要判断元素是否在当前视口中。今天我们就来看看这个问题。今天的主要内容包括:使用元素位置判断元素是否在当前视图区使用IntersectionObserver判断元素是否在当前视图区例子:懒加载例子:无限滚动实用npm包推荐文字一、使用元素position判断元素是否在当前视图区域这个方法实现起来比较简单,下面一步步来。第一:编写一个util函数isVisible,它只接受一个参数,element。exportconstisVisible=(el)=>{};使用getBoundingClientRect获取元素的位置constrect=el.getBoundingClientRect();会找到窗口的高度和宽度constvWidth=window.innerWidth||document.documentElement.clientWidth;constvHeight=window.innerHeight||文档.documentElement.clientHeight;编写另一个函数,基本上接收x和y点并使用elementFromPoint函数返回元素。constelementFromPoint=function(x,y){返回document.elementFromPoint(x,y);};检查元素是否在窗口内://如果不在视口内则返回falseif(rect.right<0||rect.bottom<0||rect.left>vWidth||rect.top>vHeight){returnfalse;}边界检查://如果它的四个角中的任何一个角可见则返回真||el.contains(efp(rect.right,rect.bottom))||el.contains(efp(rect.left,rect.bottom)));完整代码:exportconstisVisible=(el)=>{constrect=el.getBoundingClientRect();常量vWidth=window.innerWidth||文档.documentElement.clientWidth;常量vHeight=window.innerHeight||文档.documentElement.clientHeight;constefp=function(x,y){返回document.elementFromPoint(x,y);};//如果它不在视口中则返回falseif(rect.right<0||rect.bottom<0||rect.left>vWidth||rect.top>vHeight){返回false;}//如果它的四个角中的任何一个角可见则返回真.contains(efp(rect.right,rect.bottom))||el.contains(efp(rect.left,rect.bottom)));};用法:import{isVisible}from'../utils';//...constele=document.getElementById(id);返回可见(ele);逻辑并不复杂,只是多介绍2.使用IntersectionObserver判断元素是否在当前视口内IntersectionObserver是一种更高效的方式。你为什么这么说?假设您想跟踪DOM树中的元素并在它进入可见窗口时得到通知。这个功能可以通过绑定scroll事件或者使用定时器来实现,然后在回调函数中调用元素的getBoundingClientRect获取元素的位置。但是,此实现的性能极差。因为每次调用getBoundingClientRect都会强制浏览器重新计算整个页面的布局,可能会导致您的网站出现相当大的闪烁。如果您的站点加载到iframe中,并且您想知道用户何时可以看到某个元素,这几乎是不可能的。单一来源模型和浏览器不会让您在iframe中获取任何数据。对于经常在iframe中加载的广告页面,这是一个非常常见的问题。IntersectionObserver就是为此而生的。它使检查元素是否可见更加有效。IntersectionObserver让您知道观察到的元素何时进入或离开浏览器的可见窗口。使用IntersectionObserver也很简单,两步:创建IntersectionObserverconstobserver=newIntersectionObserver((entries,observer)=>{entries.forEach((entry)=>{//...console.log(entry);//目标元素://entry.boundingClientRect//entry.intersectionRatio//entry.intersectionRect//entry.isIntersecting//entry.rootBounds//entry.target//entry.time});},options);将元素传递给IntersectionObserverconstelement=document.querySelector('.element');observer.observe(element);entries参数将传递给您的回调函数,它是一个IntersectionObserverEntry对象数组。每个对象都包含您观察到的元素之一的更新交叉点数据。输出中最有用的属性是:isIntersectingtargetintersectionRectisIntersecting:当元素与默认根(在本例中为视口)相交时将为真的可见部分。这将包含有关元素的信息,它的高度、宽度、视口位置等。在线演示:https://codepen.io/myogeshcha...更多有用的属性现在我们知道:当观察到时,回调函数将被触发一次元素部分进入可见窗口,另一次离开可见窗口。这回答了这个问题:元素X是否在可见窗口中。但在某些情况下,仅此还不够。这个时候,轮到门槛出现了。它允许您定义intersectionRatio阈值。每次intersectionRatio传递这些值时,都会调用您的回调函数。阈值的默认值为[0],这是默认行为。如果我们将阈值更改为[0,0.25,0.5,0.75,1],我们将在元素的每四分之一变得可见时收到通知:还有一个属性没有在上面列出:rootMargin.rootMargin允许您指定距离元素,允许您有效地扩大或缩小相交区域。这些外边距使用CSS样式的字符串,例如:10px20px30px40px,依次指定上、右、下、左外边距。newIntersectionObserver(entries=>{//dosomethingwithentries},{//options//用于计算交集面积的根元素//如果没有提供,则使用顶层文档根的可见窗口:null,//同margin,可以是1、2、3、4个值,也可以是负值。//如果显式指定了根元素,该值可以使用百分比,即占size的百分比根元素。//如果根元素没有指定元素,使用百分比会出错rootMargin:"0px",//触发回调函数的临界值,指定0比1的比例,也可以是一个数组。//它的值是被观察元素的可见面积/总面积。//当可见比例超过这个值时,会调用回调函数。threshold:[0],});需要注意的一件事:IntersectionObserver不是完全精确到像素级别,也不是低延迟。用它来实现类似依赖滚动效果的动画是注定要失败的。因为当调用回调函数时,那些数据严格来说已经过期了。3.示例:延迟加载(lazyload)有时候,我们希望某些静态资源(比如图片)只在用户向下滚动进入视口时加载,这样可以节省带宽,提高网页性能。这称为“延迟加载”。使用IntersectionObserverAPI,很容易实现。functionquery(selector){returnArray.from(document.querySelectorAll(selector));}constobserver=newIntersectionObserver(function(changes){changes.forEach(function(change){varcontainer=change.target;varcontent=container.querySelector('template').content;container.appendChild(content);observer.unobserve(container);});});query('.lazy-loaded').forEach(function(item){观察者。观察(项目);});上述代码中,只有当目标区域可见时,模板内容才会被插入到真实的DOM中,从而触发静态资源的加载。4.例子:无限滚动无限滚动(infinitescroll)的实现也很简单:constintersectionObserver=newIntersectionObserver(function(entries){//如果不可见,则返回if(entries[0].intersectionRatio<=0)return;loadItems(10);console.log('Loadednewitems');});//开始观察intersectionObserver.observe(document.querySelector('.scrollerFooter'));无限滚动时,最好有页脚栏。一旦页脚可见,则意味着用户已到达页面底部,新项目将加载到页脚前面。这样做的好处是:不需要再次调用observe()方法,可以继续使用已有的IntersectionObserver。5.实用的npm包推荐今天主题相关的npm包推荐:react-visibility-sensor地址:https://www.npmjs.com/package...用法也很简单:importVisibilitySensorfrom"react-能见度传感器”;functiononChange(isVisible){console.log('Elementisnow%s',isVisible?'visible':'hidden');}functionMyComponent(props){return(
