实现图片懒加载的两种方案
常见的懒加载方案
这个方案是借助滚动距离来判断的,获取 document.documentElement 的 scrollTop 与需要懒加载的图片标签的 offsetTop 进行比较,看图片是否进入可视区域,从而实现图片加载。
代码如下:
// 封装一个获取图片到顶部的距离
function getTop(elm){
let tempTop = elm.offsetTop;
//如果包裹 img 的父元素离顶部也有距离,那么也要把它加上
while(elm = elm.offsetParent) { //注意这儿 offsetParent 返回的是元素不是数值,同时这儿是赋值语句
tempTop += elm.offsetTop;
}
return tempTop;
}
function lazyLoad(imgs){
//获取可视区域高度
let viewHeight = document.documentElement.clientHeight || window.innerHeight;
//获取滚动高度
let scrollTopVal = document.documentElement.scrollTop || document.body.scrollTop;
for(var i = 0; i < imgs.length; i++){
if((viewHeight + scrollTopVal) > getTop(imgs[i])){
imgs[i].src = imgs[i].getAttribute('data-src')
}
}
}
// 使用
// 给你需要懒加载的图片设置一个类名'lazy-load'
let imgs = document.querySelectorAll('.lazy-load');
lazyLoad(imgs);
// 进行滚动监听
window.onscroll = function() {
lazyLoad(imgList);
}
使用交叉观察者实现懒加载
浏览器自带的 API IntersectionObserver 进行检测,十分方便
先贴上代码实现:
// 给你需要懒加载的图片设置一个类名'lazy-load'
let imgs = document.querySelectorAll('.lazy-load');
// 实例化一个交叉观察者对象
let obsever = new IntersectionObserver((entries) => {
entries.forEach((item, index, arrSelf) => {
// 判断检测的对象是否进入可视窗口
if(item.isIntersecting) {
// 进入后进行图片地址赋值
item.target.src = item.target.dataset.src;
// 实现图片加载后不在进行检测
obsever.unobserve(item.target);
}
});
});
// 注册观察需要懒加载的 img
imgs.forEach((item) => {
obsever.observe(item);
});
IntersectionObserver
用法
new IntersectionObserver(callback, options);
- callback 是一个回调函数,接受一个参数,返回当前已监听并且发生了交叉的目标集合。
- options 配置参数,可选,是一个对象。
new IntersectionObserver((entries) => {
}, {
// options 参数
});
entries 当前已监听并且发生了交叉的目标集合,我们可以把它用来循环,参数表对于 entries 这个集合里面的每个对象都有如下常用的参数:
entries 下的对象
属性 | 说明 |
---|---|
boundingClientReact | 元素的空间信息,就是位置信息这些个 |
intersctionRatio | 元素可见区域的占比 |
isInterscting | 判断元素是否交叉,也就是元素在可视区是否可见了 |
target | 目标节点,跟我们使用事件的 event.target 效果一样 |
options 一个配置对象
属性 | 说明 |
---|---|
root | 指定父元素,默认为视窗 |
rootMargin | 触发交叉的偏移值,默认为 0px 0px 0px 0px。(上左下右,正数为向外扩散,负数则向内收缩) |
IntersectionObserver 的实例对象的常用方法
名称 | 说明 | 参数 |
---|---|---|
observe | 开始监听一个目标元素 | DOM 节点 |
unobserve | 停止监听一个目标元素 | DOM 节点 |
takeRecords | 返回所有监听的目标元素集合 | |
disconnect | 停止监听所有元素 |
封装一个支持多浏览器的懒加载方案
IntersectionObserve 这个 API 用起来确实方便,但是还是有一部分浏览器不支持,所以需要封装判断下。
function getTop(elm){
let tempTop = elm.offsetTop;
//如果包裹 img 的父元素离顶部也有距离,那么也要把它加上
while(elm = elm.offsetParent) { //注意这儿 offsetParent 返回的是元素不是数值,同时这儿是赋值语句
tempTop += elm.offsetTop;
}
return tempTop;
}
function lazyLoad(imgs){
//获取可视区域高度
let viewHeight = document.documentElement.clientHeight;
//获取滚动高度
let scrollTopVal = document.documentElement.scrollTop || document.body.scrollTop;
console.log(viewHeight, scrollTopVal)
for(var i = 0; i < imgs.length; i++){
if((viewHeight + scrollTopVal) > getTop(imgs[i])){
imgs[i].src = imgs[i].getAttribute('data-src')
}
}
}
function useIntersectionObserve(imgList) {
let obsever = new IntersectionObserver((entries) => {
entries.forEach((item, index, arrSelf) => {
console.log(entries)
if(item.isIntersecting) {
item.target.src = item.target.dataset.src;
obsever.unobserve(item.target);
}
});
});
imgList.forEach((item) => {
obsever.observe(item);
});
}
export default {
// 这两个个方案要求 img 标签有默认的高度,不然当一个页面出来就展示图片的情况下会一下展示很多图片,
// 所以在使用懒加载方案时给 img 加默认高度
// 单独使用普通懒加载方案
lazyloadDefault(selector) {
let imgList = document.querySelectorAll(selector);
lazyLoad(imgList);
window.onscroll = function() {
lazyLoad(imgList);
}
},
// 单独使用 IntersectionObserve
lazyLoadIntersectionObserver(selector) {
let imgList = document.querySelectorAll(selector);
let obsever = new IntersectionObserver((entries) => {
entries.forEach((item, index, arrSelf) => {
console.log(entries)
if(item.isIntersecting) {
item.target.src = item.target.dataset.src;
obsever.unobserve(item.target);
}
});
});
imgList.forEach((item) => {
obsever.observe(item);
});
},
// 多浏览器适配方案
lazyLoadPolyfill(selector) {
let imgList = document.querySelectorAll(selector);
if(!window.IntersectionObserver) {
lazyLoad(imgList);
window.onscroll = function() {
lazyLoad(imgList);
}
}else {
useIntersectionObserve(imgList)
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 前端水印剖析
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论