图片的预加载就是在加载大图片前,先显示一个loading.gif,就算在网络比较慢的时候也能让人知道正在加载,总比啥反应都没有强。
下面这段代码就是预加载的一个简单的实现,假设先不处理加载图片时的onError,onAbort,超时的问题。
只关注代码的结构。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button id='btnLoadImg'>加载图片</button>
<br>
<div id='imgContainer'>
</div>
<br>
<script type='text/javascript' src="./jquery-1.11.3.min.js"></script>
<script type='text/javascript'>
$(document).ready(function(){
$('#btnLoadImg').bind('click', doLoadImg);
});
function doLoadImg(){
var eleImg = createImgElement();
document.getElementById('imgContainer').appendChild(eleImg);
loadImg(eleImg, 'http://img.wanchezhijia.com/A/2015/3/20/17/11/de63f77c-f74f-413a-951b-5390101a7d74.jpg');
}
//创建img标签
//这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。
var createImgElement = (function(){
var index = 0;
return function() {
var eleImg = document.createElement('img');
eleImg.setAttribute('width', '200');
eleImg.setAttribute('heght', '150');
eleImg.setAttribute('id', 'img' + index++);
return eleImg;
};
})();
//预加载图片
//给img标签设一个加载图片,通过Image对象预先加载实际图片加载完成后设到img标签上
function loadImg(img, src) {
var imgCache = new Image();
imgCache.onload = function(){
img.src = this.src;
};
img.src = 'loading.gif';
imgCache.src = src;
}
</script>
</body>
</html>
以下预加载的代码功能上是满足了,但是它的职责包含了预加载和加载两个职责,违反了“单一职责原则”。所谓的职责就是“会发生的变化”,如果网速的 问题不再是问题或者加载的图片的分辨率被控制在很小的时候等,需要去掉预加载的功能,这时候就要修改loadImg的代码,就要重新跑所有相关的测试,即 违反了“开闭原则”,又增加测试工作。
function loadImg(img, src) {
var imgCache = new Image();
imgCache.onload = function(){
img.src = this.src;
};
img.src = 'loading.gif';
imgCache.src = src;
}
加载和预加载其实就是代理模式的一种,代理模式可以理解成,你会开车但是因为某种原因只能找人代理开车,因为不了解MM的喜好找人代理送花。
将预加载功能改成代理模式可以理解成:本体先显示个门面,找代理去加载,加载完了,告诉本体,本体把图片贴上去就行了。
既然如此,本体的职责就很简单了,就往门面上贴。
加载本体函数:
function loadImg(img, src) {
img.src = src;
}
做一个预加载代理函数:
function loadImgProxy(img, src){
var imgCache = new Image();
imgCache.onload = function(){
loadImg(img, this.src);
};
loadImg(img, 'loading.gif');
imgCache.src = src;
}
在代理函数中,先让本体加载loading.gif,等大图加载完了再让本体加载实际图片。
代理还是和本体函数的接口参数是一致的,职责分的比较清晰,如果到时候要把预加载去掉,也不需要修改本体和代理的代码,只需要在调用的地方把代理的名字换成本体的名字即可。
可以适当的把代理函数改一下,可以让其适应加载多个图片的场景,其实就是做一个闭包,把缓存Image对象变成私有即可:
var loadImgProxy = (function(){
var imgCache = new Image();
return function(img, src){
imgCache.onload = function(){
loadImg(img, this.src);
};
loadImg(img, 'loading.gif');
imgCache.src = src;
};
})();
修改后的完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button id='btnLoadImg'>加载图片</button>
<br>
<div id='imgContainer'>
</div>
<br>
<script type='text/javascript' src="./jquery-1.11.3.min.js"></script>
<script type='text/javascript'>
$(document).ready(function(){
$('#btnLoadImg').bind('click', doLoadImg);
});
function doLoadImg(){
var eleImg = createImgElement();
document.getElementById('imgContainer').appendChild(eleImg);
//使用代理函数进行加载
//如果某一天不需要预加载了,就把loadImgProxy换成loadImg即可
loadImgProxy(eleImg, 'http://img.wanchezhijia.com/A/2015/3/20/17/11/de63f77c-f74f-413a-951b-5390101a7d74.jpg');
}
//创建img标签
//这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。
var createImgElement = (function(){
var index = 0;
return function() {
var eleImg = document.createElement('img');
eleImg.setAttribute('width', '200');
eleImg.setAttribute('heght', '150');
eleImg.setAttribute('id', 'img' + index++);
return eleImg;
};
})();
//加载图片本体函数
function loadImg(img, src) {
img.src = src;
}
//加载图片代理函数
var loadImgProxy = (function(){
var imgCache = new Image();
return function(img, src){
imgCache.onload = function(){
loadImg(img, this.src);
};
loadImg(img, 'loading.gif');
imgCache.src = src;
};
})();
</script>
</body>
</html>