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

图片懒加载无限滚动-IntersectionObserver

时间:2023-03-27 15:27:09 JavaScript

IntersectionObserver实现图片懒加载无限滚动后台图片懒加载和滚动加载数据的需求一直存在。比较常见的方法是监听页面或容器的滚动事件,实时计算节点和滚动事件。容器边界之间的关系来实现不同的加载逻辑。通常,EventListener用于绑定监听事件,Element.getBoundingClientRect()用于获取相关元素的边界信息,然后两者都在主线程上运行,会占用一定的资源和性能,所以事件触发频繁并且方法被调用可能会导致性能问题,这种检测方法极其怪异和不优雅。IntersectionObserverAPI提供了异步检测目标元素与祖先元素或视口相交区域大小变化的方法,它会注册一个回调函数,当两个被监测元素的相交区域大小发生变化时(包括被监听的元素进入或退出另一个元素),回调方法会被触发执行。这样主线程就不需要浪费资源去监控滚动和实时计算,浏览器会自行优化元素交集管理。IntersectionObserverAPI介绍创建一个IntersectionObserver实例,并添加一个监听对象:constel=documen.querySelector('#target')//被监听的元素constobserver=newIntersectionObserver(callback,options)//创建一个实例观察者.observe(el)//开始监听元素其中callback为必须配置的回调函数,options用于配置观察者实例的监听环境,observe()方法用于注册监听。MDN文档Intersectionobserveroptions如果不指定options,则观察者实例将以当前视口为根,无边距,阈值为0,即即使像素变化也会触发回调函数。可配置的参数(来自MDN)如下:root指定根(root)元素,用于检查目标的可见性。必须是目标元素的父元素。如果未指定或为null,则默认为浏览器窗口rootMargin根元素的边距。类似于CSS中的margin属性,如“10px20px30px40px”(上、右、下、左)。如果指定了root参数,rootMargin也可以使用百分比取值。该属性的值作为根元素与目标相交时的计算相交区域范围。使用这个属性可以控制根元素每一边的收缩或扩展。默认值为0。阈值可以是单个数字或数字数组。当目标元素和根元素的交集度达到这个值时,IntersectionObserver注册的回调函数就会被执行。如果只想检测目标元素在根元素中的可见性何时超过50%,则可以为此属性指定值0.5。如果你希望目标元素在根元素中每可见度的25%执行一次回调,那么你可以指定一个数组[0,0.25,0.5,0.75,1],默认值为0。一个值为1.0表示只有当目标完全出现在根元素中时才会执行回调。Intersectionobservercallback回调函数会在以下情况发生时被调用:当Observer第一次监听目标元素时(执行observe()方法时)每当目标(target)元素与设备窗口相交或其他指定元素(根)时(当该元素的可见比例超过指定阈值时)回调函数在调用时会收到两个参数:目标元素与其根元素容器在特定过渡时刻的交集状态。IntersectionObserverEntry的实例作为entries参数传递给IntersectionObserver实例的回调函数,它具有以下只读属性:boundingClientRect:返回包含目标元素边界信息的DOMRectReadOnly。边界计算方法同Element.getBoundingClientRect()intersectionRatio:返回intersectionRect与boundingClientRect的比值,取值范围为0到1intersectionRect:返回一个DOMRectReadOnly,用于描述根元素和目标元素的相交面积描述目标元素与根元素是否存在交集区域rootBounds:返回一个DOMRectReadOnly,用于描述交集观察器(intersectionobserver)中的根元素target:交集区域随根时间变化的元素(Element):返回来自IntersectionObserverIntersectionO的记录bserverIntersectionObserver有以下方法:disconnect():停止对该实例的所有监听observe():开始监听takeRecords():返回所有观察目标的IntersectionObserverEntry对象数组unobserve():停止监听特定元素IntersectionObserverAPI实战图片延迟加载当我们访问一个图片较多的网页时,往往会因为图片数量较多而导致加载速度变慢。大量img图片导致页面渲染受阻。当需要花费大量精力来加载所有图像和页面时,用户早就消失了。另一方面,如果用户只浏览了网页的前面部分就离开了,很多已经加载但没有显示在viewport区域的图片因为在网页的底部,会大大增加压力在服务器上,但用户不会看,这是浪费时间性能。为了解决以上问题,就需要引入图片的懒加载。懒加载其实很好理解,重点就在一个“懒”字上。当用户滚动对应的可见区域时,如果可见区域有图片,则加载,可见区域外还没有加载的图片先不加载,等用户滚动时再加载他们的可见区域,否则他们根本不会被加载。这样一来,网页渲染的性能得到了极大的提升,减少了不必要的浪费。通过IntersectionObserverAPI,我们可以轻松实现图片的懒加载。本例使用原生HTML+JavaScript实现,大致思路如下:图片占位符:这里使用HTML5的dataset属性,将真实图片地址赋值给data-src属性创建IntersectionObserver实例并配置回调函数constobserver=newIntersectionObserver(entries=>{for(constiofentries){if(i.isIntersecting){//当目标元素出现在视图中时constimg=i.target;consttrueSrc=img.getAttribute("data-src");setTimeout(()=>{img.setAttribute("src",trueSrc);//方便展示懒加载效果},1500);observer.unobserve(img);//停止监听这个元素}}});在初始化实例的时候,可以使用箭头函数直接绑定回调函数,而在回调函数中,我们设置好图片的真实地址后,就应该停止对图片的监听,避免不必要的处理。监听所有img元素constimages=document.getElementsByTagName("img");for(constiofimages){observer.observe(i);}需要注意的是,设置监听应该在渲染之后进行page结构,即脚本应该在window.onload时执行,因为调用observer.observe()时,回调函数会执行一次。此时元素没有初始化,高度为0,会执行if语句中的脚本。如果无法监听window.onload事件,可以为元素设置基本宽高,保证脚本初始化时只有部分元素在视图中。实际效果如下图gif所示:可以看到刷新页面后,第一次加载的只有三四张出现在视图中的图片。随着页面的滚动,出现的img标签的src属性被修改为真实的图片地址。Infinitescrolling内容无限滚动其实就是在用户滚动到接近内容底部的时候主动加载新的数据,不需要用户翻页,给用户一种网页可以无限滚动的错觉。这里提供两种实现思路,都是在IntersectionObserver的回调函数中进行相关的判断和操作。可以自己尝试实现:存储图片信息的列表,在回调函数中判断如果当前元素是列表的最后一项,则触发加载下一页的数据,合并新加载的数据与原始列表。加载数据后,需要监听新添加的元素页面,添加页脚栏(也称为哨兵)。一旦页脚栏可见,就意味着用户已经到达。在页面底部,加载新数据并将其放在页脚列的前面。下图是一个无限加载的例子。使用UnsplashAPI,每次会加载几张随机图片: