几个月前实现的一个图片懒加载库lazyload.js
,github 地址。
需要实现的效果
相信大家都在网页中体验过图片懒加载,它应该有这样的效果:当图片进入我们的可视区时,加载这些图片。其原因相信大家都懂的——提高用户体验。
那么在代码中,我们就应该实现一下几点功能:
1.当页面加载完成时,先对可视区的图片进行加载;
2.给 scroll
及 resize
事件添加监听;
3.当上述事件触发时,对在可视区的图片进行加载,并将加载过的图片移除。
具体实现
经过上面的分析,我们一步一步来实现图片懒加载。首先,我们先制定一些规则:
1.需要懒加载的图片标签应该带有值为 lazyload-img
的类;
2.需要懒加载的图片标签应该带有属性 data-src
,其值为图片的链接。
定义懒加载构造函数
function LazyLoad() {
// 将所有需要懒加载的图片缓存在 imgList 数组中
this.imgList = [].slice.call(document.querySelectorAll(".lazyload-img"));
// 进行初始化操作
this.init();
}
接下来想想我们的 init
方法应该做些什么?图片的加载是通过事件触发实现的,所以我在初始化的方法主要做的事情就是添加事件监听。
把 init
方法的实现放到最后来讲,接下来先来实现判断图片是否在可视区的方法 isInViewport
。
isInViewport方法的实现
在上代码前先来讲讲 getBoundingClientRect
这货,不知道大家对她了解多少。MDN上给出的定义是这样的:
Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。
什么意思呢,就是调用了这个方法后,返回了一个包含元素的宽高及其相对于浏览器视口左上角的位置信息。嗯.. 没看懂:( 简单点说呢,这个方法返回了一个对象,长这样:
{
bottom: xx,
left: xx,
right: xx,
top: xx,
height: xx,
width: xx
}
如果还是不太明白,可以直接到MDN看看。建议大家直接在控制台试试,说得再多不如动手试一试。
那了解这个方法后,有啥用?当然是用来判断图片是否在可视区咯,那我们来看看判断的逻辑:
LazyLoad.prototype.isInViewport = function (img) {
var clientH = document.documentElement.clientHeight, // 浏览器视口高度
clientW = document.documentElement.clientWidth, // 浏览器视口宽度
imgPosOb = img.getBoundingClientRect(), // 获取img的位置信息
imgT = imgPosOb.top,
imgL = imgPosOb.left,
imgH = imgPosOb.height,
imgW = imgPosOb.width;
// 判断逻辑
// 不清楚的同学可以在草稿纸上画个图
if( (imgT > -imgH && imgT < clientH) && (imgL > -imgW && imgL < clientW) ) {
// inViewport
return true;
} else {
return false;
}
}
我们还有什么功能没实现?当然还有最重要的加载!
实现图片加载
这一步没啥好说的,直接上代码:
// 参数 imgList 是要加载的图片列表
LazyLoad.prototype.loadImg = function (inVpImgList) {
var len = inVpImgList.length;
var src = '';
for (var i = 0; i < len; i++) {
src = inVpImgList[i].getAttribute("data-src");
inVpImgList[i].src = src;
inVpImgList[i].removeAttribute("data-src");
this.removeItem(inVpImgList[i]);
}
};
// 不要忘了这一步
// 把加载过的图片移除
LazyLoad.prototype.removeItem = function (img) {
var idx = this.imgList.indexOf(img);
if(idx > -1) {
this.imgList.splice(idx, 1);
}
};
init方法的实现
最后,来完成我们的初始化方法:
LazyLoad.prototype.init = function () {
var self = this;
// callback函数即我们定义的事件触发后的回调函数
function callback() {
var imgInVp = [];
var len = self.imgList.length;
for (var i = 0; i < len; i++) {
if(self.isInViewport(self.imgList[i])) {
imgInVp.push(self.imgList[i]);
}
}
self.loadImg(imgInVp);
}
callback();
document.addEventListener("scroll", callback, false);
window.addEventListener("resize", callback, false);
};
可能有哪位细心的小伙伴会发现我在 init
方法中调用了一次 callback
。让我们再回头看看功能分析的第一点,你就明白了。那到这咱就完事了?不,还得优化一下。
优化
我们知道当我们在短时间内,连续地改变浏览器窗口大小或者滚动页面,会连续多次地触发 resize
和 scroll
事件。而这是我们可以优化的地方,这里我们使用函数去抖,调整后的代码如下:
LazyLoad.prototype.init = function () {
var self = this,
timer = null;
function callback() {
timer && clearTimeout(timer);
timer = setTimeout(function () {
var imgInVp = [];
var len = self.imgList.length;
for (var i = 0; i < len; i++) {
if(self.isInViewport(self.imgList[i])) {
imgInVp.push(self.imgList[i]);
}
}
self.loadImg(imgInVp);
}, 100);
}
callback();
document.addEventListener("scroll", callback, false);
window.addEventListener("resize", callback, false);
};
至此,也算是实现了一个简单的图片懒加载库了,如有不足之处欢迎指正,大家一起交流交流 :)