图片懒加载和预加载技术

undefined

图片懒加载和预加载技术,从名字上就可以看出它们是一对干同一行却不同研究方向的兄弟。之前学的时候大致了解了一遍,后来太久没运用上,就忘了….于是又花了时间去补回来。干脆记录一下好了~

概念的解析

懒加载也叫延迟加载:顾名思义,只有图片要用到的时候它才去加载,懒到要死,绝不提前搞事情。

预加载:顾名思义,我猜到你以后要用这张图片,干脆先给你加载出来,省事又省心。

两者的本质:就是拖延症与未雨绸缪的对决啦。懒加载可以缓解服务器的压力,预加载会增加服务器的压力。

那就很奇怪了。为什么,两个完全相反的概念都可以被运用呢?很简单,场景复杂嘛。

  • 假如有一个页面,有几千张的图片(百度搜图),为了提高用户体验只加载个百来张就展示给用户看,避免卡顿。
  • 假如又有一个页面,用户在看漫画。漫画的浏览速度是很快的,一页又一页,这时候明明看到高潮了,按下一页的时候页面却卡住了,是不是让你很不爽?没错,于是为了提高用户体验又必须提前加载这些图片。

于是你就懂了,任何前端技术,不是为了方便开发,加快开发速度。就是为了提高用户体验,用户怎么爽怎么来。好了,搞清楚这些东西我们就可以开始捣鼓啦。

图片懒加载

原理

关于懒加载,通过上面的场景描述,你肯定就发现了要怎么处理这个玩意了。

首先,必须要监听用户的某项操作,当用户滚动到底部,或者说点击某个位置的时候,便跑去告诉浏览器用户爸爸要看图片啦,赶紧去搞过来。————

其次,那放图片的位置咋办呢?随便搞个东西先填住那个洞,到时候狸猫换太子就好了。狸猫就是我们常说的占位符图片。而太子则一般是利用自定义属性data-img来存放。然后当用户触发时间后,就将data-img放到src里面去。

源码

hmm,看时间写个demo吧

预加载

预加载技术其实就是大多是利用了new Image()这个对象来实现提前加载,本质上只要有src属性的标签也都是可以实现的。由于Image对象有onload方法可以判断是否加载完调用回调函数进行自定义操作,所以更加讨喜。


18年5.11日跑回来订正..之前学的时候错的有点天真就是认为预加载利用的是动态插标签,预先得到内容。。。但其实这绕了个大弯,明明就是一个ajax获取资源就行了。至于图片能获取到宽高所以利用Image对象才会更加方便罢了。

同样的,还是那个问题,图片还没搞到手,我咋知道宽高,怎么给它设置呢。在之前一篇文章中根据图片路径获取图片大小就有提到,我们只需要抓取图片的文件头,浏览器就可以知道它的宽高。(方法是用记时器轮循宽高变化)。

1
2
3
4
let img = new Image();
img.src = path;// 设置图片路径
let width = img.width;
let height = img.height;

原理介绍完了,但是还有小问题要处理。假如图片已经存在浏览器的缓存中,那么它并不会触发onload事件,这时候我们需要监听img.complete这个属性来判断是否存在缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function loadImage(url,callback) {
var img = new Image();
img.src = url;
if(img.complete) { // 如果图片已经存在于浏览器缓存,直接调用回调函数
callback.call(img);
return; // 直接返回,不用再处理onload事件
}
img.onload = function(){
img.onload = null;
callback.call(img); // 调用回调函数, .call()这个方法就是那个设置this的方法呀
}
}

源码

嘻嘻,不是我自己写的啦。多看看别人的源码,可以发现人家在处理细节的地方做得真的很好。

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
var imgReady = (function(){
var list = [],
intervalId = null;
// 用来执行队列
var queue = function(){
for(var i = 0; i < list.length; i++){
list[i].end ? list.splice(i--,1) : list[i]();
}
!list.length && stop();
};
// 停止所有定时器队列
var stop = function(){
clearInterval(intervalId);
intervalId = null;
}
return function(url, ready, error) {
var onready = {},
width,
height,
newWidth,
newHeight,
img = new Image();
img.src = url;
// 如果图片被缓存,则直接返回缓存数据
if(img.complete) {
ready.call(img);
return;
}
width = img.width;
height = img.height;
// 加载错误后的事件
img.onerror = function () {
error && error.call(img);
onready.end = true;
img = img.onload = img.onerror = null;
};
// 图片尺寸就绪
var onready = function() {
newWidth = img.width;
newHeight = img.height;
if (newWidth !== width || newHeight !== height ||
// 如果图片已经在其他地方加载可使用面积检测
newWidth * newHeight > 1024
) {
ready.call(img);
onready.end = true;
};
};
onready();
// 完全加载完毕的事件
img.onload = function () {
// onload在定时器时间差范围内可能比onready快
// 这里进行检查并保证onready优先执行
!onready.end && onready();
// IE gif动画会循环执行onload,置空onload即可
img = img.onload = img.onerror = null;
};
// 加入队列中定期执行
if (!onready.end) {
list.push(onready);
// 无论何时只允许出现一个定时器,减少浏览器性能损耗
if (intervalId === null) {
intervalId = setInterval(queue, 40);
};
};
}
})();

调用方式:

1
2
3
imgReady('http://img01.taobaocdn.com/imgextra/i1/397746073/T2BDE8Xb0bXXXXXXXX-397746073.jpg',function(){
  alert('width:' + this.width + 'height:' + this.height);
});

有一个遗留问题

最近在开发中,发现如果用input图片,然后动态设置loading图像,好像会有明显的卡顿。没找到解决方法。先mark下来吧。暂时的做法是监听input.onchange事件,然后用css3进行设置,尽可能减少延迟。

在一篇文章中提到,将input设置接受的图片对象。越详细越好,可以避免一些不必要的文件后缀查询。

缩略图

补充自8月
今天突然想起来以前的一个网站的效果。利用的也是懒加载技术,但是从用户体验上会更好。那就是先加载图片的缩略图,然后在加载完毕时候再替换。(就是懒加载啦)但是为什么要补充呢!!因为这个小技巧还是挺好玩的,而且有个小地方要注意: 缩略图比较难看,为了有更好的用户体验,可以给缩略图加个高斯模糊filter: blur(4px),图片加载完毕修改img src属性后,再将高斯模糊渐渐过渡到filter: blur(0),图片就清晰了