------------------------------------------------------------------------------------------------html
当页面中须要一次性载入不少图片的时候,每每都是须要用懒加载的。
git
咱们都知道HTML中的 <img>
标签是表明文档中的一个图像。。说了个废话。。github
<img>
标签有一个属性是 src
,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。若是没有 src
属性,就不会发送请求。api
嗯?貌似这点能够利用一下?数组
我先不设置 src
,须要的时候再设置?浏览器
nice,就是这样。性能优化
咱们先不给 <img>
设置 src
,把图片真正的URL放在另外一个属性 data-src
中,在须要的时候也就是图片进入可视区域的以前,将URL取出放到 src
中。bash
------------------------------------------------------------------------------------------------app
仔细观察一下, <img>
标签此时是没有 src
属性的,只有 alt
和 data-src
属性。
函数
alt 属性是一个必需的属性,它规定在图像没法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,能够经过 HTMLElement.dataset
来访问。
方法一
网上看到好多这种方法,稍微记录一下。
经过 document.documentElement.clientHeight
获取屏幕可视窗口高度
经过 document.documentElement.scrollTop
获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
经过 element.offsetTop
获取元素相对于文档顶部的距离
而后判断②-③<①是否成立,若是成立,元素就在可视区域内。
方法二(推荐)
经过 getBoundingClientRect()
方法来获取元素的大小以及位置,MDN上是这样描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为 ClientRect
的 DOMRect
对象,包含了 top
、 right
、 botton
、 left
、 width
、 height
这些值。
MDN上有这样一张图:
能够看出返回的元素位置是相对于左上角而言的,而不是边距。
咱们思考一下,什么状况下图片进入可视区域。
假设 constbound=el.getBoundingClientRect();
来表示图片到可视区域顶部距离; 并设 constclientHeight=window.innerHeight;
来表示可视区域的高度。
随着滚动条的向下滚动, bound.top
会愈来愈小,也就是图片到可视区域顶部的距离愈来愈小,当 bound.top===clientHeight
时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在 bound.top<=clientHeight
时,图片是在可视区域内的。
咱们这样判断:
这里有个+100是为了提早加载。
function checkImgs() {
const imgs = document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el => {
if (isInSight(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (!el.src) {
const source = el.dataset.src;
el.src = source;
}
}
复制代码
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不须要遍历全部的图片,只须要遍历未加载的图片便可。
在相似于滚动条滚动等频繁的DOM操做时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减小一些过快的调用来节流。
基本步骤:
获取第一次触发事件的时间戳
获取第二次触发事件的时间戳
时间差若是大于某个阈值就执行事件,而后重置第一个时间
function throttle(fn, mustRun = 500) {
const timer = null;
let previous = null;
return function() {
const now = new Date();
const context = this;
const args = arguments;
if (!previous){
previous = now;
}
const remaining = now - previous;
if (mustRun && remaining >= mustRun) {
fn.apply(context, args);
previous = now;
}
}
}
复制代码
这里的 mustRun
就是调用函数的时间间隔,不管多么频繁的调用 fn
,只有 remaining>=mustRun
时 fn
才能被执行。
------------------------------------------------------------------------------------------------
能够看出此时仅仅是加载了img1和img2,其它的img都没发送请求,看看此时的浏览器
第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
当我向下滚动,此时浏览器是这样
此时第二张图片彻底显示了,而第三张图片显示了一点点,这时候咱们看看请求状况
img3的请求发出来,然后面的请求仍是没发出~
当滚动条滚到最底下时,所有请求都应该是发出的,如图
------------------------------------------------------------------------------------------------
方法三 IntersectionObserver
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver
能够自动观察元素是否在视口内
var io = new IntersectionObserver(callback, option);
// 开始观察
io.observe(document.getElementById('example'));
// 中止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
callback的参数是一个数组,每一个数组都是一个 IntersectionObserverEntry
对象,包括如下属性:
属性
描述
time |
可见性发生变化的时间,单位为毫秒 |
rootBounds |
与getBoundingClientRect()方法的返回值同样 |
boundingClientRect |
目标元素的矩形区域的信息 |
intersectionRect |
目标元素与视口(或根元素)的交叉区域的信息 |
intersectionRatio |
目标元素的可见比例,即intersectionRect占boundingClientRect的比例,彻底可见时为1,彻底不可见时小于等于0 |
target |
被观察的目标元素,是一个 DOM 节点对象 |
咱们须要用到 intersectionRatio
来判断是否在可视区域内,当 intersectionRatio>0&&intersectionRatio<=1
即在可视区域内。
代码
function checkImgs() { const imgs = Array.from(document.querySelectorAll(".my-photo"));
imgs.forEach(item => io.observe(item));}
function loadImg(el) { if (!el.src) {
const source = el.dataset.src; el.src = source; }}
const io = new IntersectionObserver(ioes => {
ioes.forEach(ioe => { const el = ioe.target;
const intersectionRatio = ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <= 1) {
loadImg(el); } el.onload = el.onerror = () => io.unobserve(el); });});复制代码
文章来源于公众号里,如有侵权请告之!