图片懒加载组件 Ylazyload

本文将介绍一款由原生 JavaScript 编写的懒加载组件 Ylazyload, 其不依赖于 JQuery 或其他 JavaScript 框架

源码&DEMO地址: Ylazyload

实现原理

1、在图片不在可视区域的时候,所有图采用默认的占位图显示,而不是让浏览器加载所有的真实图片,从而减少服务器和客户机的带宽资源,实现按需提供服务的目的
2、在页面滚动时,不断地去检测图片是否进入了可视区域, 如果进入可视区域则将默认的占位图替换成真实图片
3、如何实现判断图片进入可视区域:

  • 先计算出我们确定的可视区域在浏览器视窗中的坐标, visibleArea
  • 在页面滚动的时候,定期去计算每张图片在浏览器视窗中的坐标, imgCoord
  • 通过对比 visibleArea 和 imgCoord 上下左右的坐标值,即可判断出图片是否在可视区域内
  • 本组建采用方法 getBoundingClientRect 获取 DOM 元素的在窗口中坐标

更详细的解释可以参见博客 细说jQuery如何实现懒加载

使用 Ylazyload

1、在使用前在页面引入 Ylazyload

1
2
<!-- 引入ylazyload -->
<script src="/static/common/lazyload/ylazyload-1.0.js"></script>

2、为需要懒加载的图片添加属性 ylazyload-src
为需要懒加载图片添加 ylazyload-src 是必须的, ylazyload-src 是真实的图片地址

1
2
3
4
5
6
7
8
9
<li>
<img class="ylazyload" src="default-img-url" ylazyload-src="real-img-url1">
</li>
<li>
<img class="ylazyload" src="default-img-url" ylazyload-src="real-img-url2">
</li>
<li>
<img class="ylazyload" src="default-img-url" ylazyload-src="real-img-url3">
</li>

3、初始化 Ylazyload

1
2
3
4
5
// page-main: 为可视容器元素的 id 属性
// function(dom, src): 回调函数
var yLazyload = new Ylazyload('page-main', function(dom, src) {
console.log('src', src)
})

4、添加需要懒加载的图片列表

1
2
3
4
5
6
7
8
//只有在使用了 appendImgs 之后才会真正实现懒加载效果
yLazyload.appendImgs()
//如果是异步 js 渲染的图片, 在图片渲染结束后,相应执行 appendImgs 方法
//刷新需要懒加载的图片列表
function renderImgList() {
...
}
yLazyload.appendImgs()

Ylazyload 参数介绍

提前申明: Ylazyload 没有参数是必填的
1、初始化: new Ylazyload(container, callback)
container: 作用是指定可视区域容器元素, 取值可为:

  • string: 容器元素的 id 属性
  • DOM Object: 容器元素的 DOM 对象
  • default: 为 document 对象

callback: 回调函数,返回当前加载真实地址的 img 元素 DOM 对象, 和 真实图片地址, function(dom, src)

  • dom: 当前替换地址的 img DOM 对象
  • src: 真实图片地址

2、添加懒加载的图片方法 appendImgs(imgs) 参数

  • string: img 元素的 class 属性
  • DOM NodeList: img 元素 DOM 对象列表
  • default: 所有拥有 ylazyload-src 属性的 img 元素
  • 如无特别需求推荐 appendImgs 方法不传参数

Ylazyload 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* author: feifeiyu
* version: 1.0
* ylazyload for img lazyload
* @param container //for init, visible area dom, type: string or DOM object, default document
* @param cb //for init, callback, can be null
* @param imgs //for appandImgs method, imgs list to lazyload, type: string or img DOM object list, can be nul
* Ylazyload blog: https://feifeiyum.github.io//2017/05/13/front-ylazyload/
*/

var Ylazyload = (function() {
//初始化组件
var YLL = function(container, cb) {
//获取可视区域 DOM object
if(typeof container === 'string') {
this.container = document.getElementById(container)
} else if(container && container.constructor.toString().indexof('Element()') > -1) {
this.container = container
} else {
this.container = document
}
//暂存待加载真实地址的 img 元素对象
this.imgList = []
this.tid = 0
cb && (this.cb = cb)
this.getVisibleArea()
this._refreshEvt()
}
//获取可视区域的坐标
YLL.prototype.getVisibleArea = function() {
try {
this.visibleArea = this.container.getBoundingClientRect()
} catch(err) {
//如果 container 是 document 对象,直接取屏幕宽高
this.visibleArea = {
top: 0,
left: 0,
bottom: window.screen.availHeight,
right: window.screen.availWidth
}
}
}
//为可视容器对象添加 scroll 事件
YLL.prototype._refreshEvt = function() {
var self = this
if(self.container.addEventListener) {
self.container.addEventListener('scroll', function() {
clearTimeout(self.tid)
//页面滚动时 不断去检测图片列表中进入可视区域的图片
self.tid = setTimeout(self.isVisible.call(self), 300)
})
window.addEventListener('resize', function() {
clearTimeout(self.tid)
self.tid = setTimeout(function() {
self.getVisibleArea.call(self)
self.isVisible.call(self)
})
})
} else if(self.container.attachEvent) {
self.container.attachEvent('onscoll', function() {
clearTimeout(self.tid)
self.tid = setTimeout(self.isVisible.call(self), 300)
})
window.attachEvent('onresize', function() {
clearTimeout(self.tid)
self.tid = setTimeout(function() {
self.getVisibleArea.call(self)
self.isVisible.call(self)
})
})
}
}
//添加需要懒加载的图片
YLL.prototype.appendImgs = function(imgs) {
var self = this
if(typeof imgs === 'string') {
imgs = document.querySelectorAll('.' + imgs)
} else {
imgs = document.querySelectorAll('img[ylazyload-src]')
}
imgs.forEach(function(item) {
self.imgList.push(item)
})
this.isVisible()
}
//检查图片列表中是否进入可视区域
YLL.prototype.isVisible = function() {
var self = this
var removeImg = []
var listLen = self.imgList.length

for(var i = 0; i < listLen; i++) {
var item = self.imgList[i]
var itemCoord = item.getBoundingClientRect()
//对比坐标判断是否进入进入可视区域
if(itemCoord.top > self.visibleArea.bottom) {
continue
} else if(itemCoord.bottom < self.visibleArea.top) {
continue
} else if(itemCoord.left > self.visibleArea.right) {
continue
} else if(itemCoord.right < self.visibleArea.left) {
continue
} else {
//如果进入可视区域,则替换去真实图片链接
self._activeImg(item)
//如果图片f已经替换了真实链接,踢出遍历列表
removeImg.push(item)
}
}
listLen = removeImg.length
for(var i = 0; i < listLen; i++) {
var index = self.imgList.indexOf(removeImg[i])
self.imgList.splice(index, 1)
}
}
//从属性 ylazyload-src 取出真实图片地址, 替换默认图片地址
YLL.prototype._activeImg = function(imgDom) {
var self = this
var realSrc = imgDom.getAttribute('ylazyload-src')
if(realSrc) {
imgDom.setAttribute('src', realSrc)
//删除图片属性 ylazyload-src
imgDom.removeAttribute('ylazyload-src')
//回调
self.cb && self.cb(imgDom, realSrc)
}
}
return YLL
}())

//如果采用模块化打包引入的时候加上这句
// module.exports = Ylazyload

END