一、图片懒加载意义
虽然当代浏览器在渲染DOM树的时候,遇到img并不会阻碍DOM树的渲染(浏览器会开辟HTTP线程请求图片资源文件),但是在生成RENDER TREE后,浏览器进行渲染的时候,会把渲染树和图片一起进行绘制
会遇到一些影响性能(影响页面第一次加载的速度)问题:
- 如果请求的图片资源过多,我们同时可以开辟的HTTP线程只有6个,这样图片资源预先的加载,会影响其他资源的请求速度
- 第一次绘制页面的时候,如果就开始绘制图片,也需要消耗很多的时间,也影响页面第一次打开的速度
解决方案:
- 我们一般都采用图片懒加载(开始需要展示图片的位置,我们基于默认图或者一个空白的盒子占位,真实图片不加载,只有当页面第一次渲染完以及滚动到当前所在的区域时候,再去加载真实的图片)
- 我们可以把图片base64(虽然也会慢一点,但是总比不做强)
二、实现方案
- 旧:基于盒子模型( 或者getBoundingClientRect() ),我们判断出是否加载图片,在滚动条滚动中随时进行判断处理(需要做节流);
- 新:IntersectionObserver基于元素的监听,一体化解决懒加载问题
/*
IntersectionObserver监听,当DOM元素出现和离开视口的时候触发回调函数
observer = new IntersectionObserver([callback], [options])
threshold 默认值是[0],我们可以基于数组的方式管控,元素相对于视口的交叉位置,在什么程度下触发
回调函数
*/
observer.observe([DOM元素]) 监听
observer.unobserve([DOM元素]) 移除监听
//例:
let observer = new IntersectionObserver( changes => {
//changes包含所有监听的DOM信息
changes.forEach( item => {
let { isIntersecting, target } = item;
if(isIntersecting) {
lazyImg(target);
observer.unobserve(target);
}
});
}, {
//控制什么时候触发回调
threshold: [1]
} )
三、无限下拉加载
/*
在整个卡片下面加一个盒子,如起名为bottomBox,设置高度为100px,监听盒子即可,如果监听到这个盒子已经
露头,说明已经到达底部,就可以再加载一次真实数据
*/
let observer2 = new IntersectionObserver(changes => {
let isLoadMore = false; //是否正在加载更多数据
let isIntersecting = changes[0].isIntersecting;
if(isIntersecting) {
//到达底部了
if(isLoadMore) return;
isLoadMore = true;
//重新发送请求即可
}
}, {
threshold: [0]
})
四、图片懒加载插件封装
/*
图片懒加载插件:只处理图片的延迟加载
规定:
1. 结构:图片放在一个盒子中,盒子是图片没有加载之前的占位;需要延迟加载的图片,src是空的,
data-image存放真实的地址,我们默认认为所有拥有data-image属性的图片都要做延迟加载
<div>
<img src="" data-img="image/1.png" />
</div>
2.导入JS后,我们可以暴露出一个API(例如init),只要执行init方法就会开始进行图片延迟加载,而且还可以支持一些配置参数:
{
threshold: [1], //什么时候延迟加载
animate: true, //是否有动画:渐隐渐现(需要给图片设置opacity/transtion样式)
attr: 'data-img', //具备哪些属性的进行延迟加载(要求属性值一定是真实存在地址)
onload: function(img){} //每一张图片加载完要触发的函数
}
*/
(function () {
constructor(options) {
//把配置项挂载到实例上
this.options = options;
//创建一个监听的实例
let config = {
threshold: options.threshold
};
this.observe = new IntersectionObserver(this.callback.bind(this), config)}
//原型方法
watch() {
let { attr, animate } = this.options;
//监听元素
let allImgs = Array.from(document.querySelectorAll(`img[${attr}]`));
allImgs.forEach(item => {
//如果需要实现渐隐渐现动画,需要设置样式
if(animate) {
item.style.opacity = 0;
item.style.transtion = "opacity .3s ease";
}
//监听图片所在的div
this.observe.observe(item.parentNode);
});
}
callback(changes){
//回调触发
changes.forEach( item => {
let { isIntersecting, target } = item;
if(isIntersecting) {
this.lazyImg(target);
observer.unobserve(target);
}
});
}
lazyImg(target){
//单张图片延迟加载
let { attr, animate, onload } = this.options;
let img = target.querySelector('img'),
trueImg = img.getAttribute(`${attr}`);
img.src = trueImg;
img.onload = () => {
//图片加载成功
img.style.display = 'block';
if(animate) {
img.offsetWidth; //目的:刷新浏览器渲染队列
img.style.opacity = 1;
};
//单张图片加载完都触发这个函数
onload.call(this, img)
img.removeAttribute(`${attr}`);
}
}
class LazyImg {
//静态对象
static init( options={} ){
//参数初始化
options = Object.assign({
threshold: [1],
animate: true,
attr: 'data-image',
onload: function(){}
}, options)
//创造类的实例
return new LazyImg(options);
}
}
if(typeof window !== 'undefined') {
window.LazyImg = LazyImg;
}
if(typeof module !=='undefined' && typeof module.exports !== 'undefined'){
module.exports = LazyImg;
}
})();
LazyImg.init();
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!