目的
在图片的加载过程中,提供定义图片加载成功或加载失败/超时时的回调函数,并确保执行。
动机
原生JavaScript已经对Image对象提供了onload和onerror注册事件。但在浏览器缓存及其他因素的影响下,用户在使用回退按钮或者刷新页面时onload事件常常未能稳定触发。在我开发的相册系统中,我希望图片能根据自定义的大小显示以免导致页面变形,例如最宽不得超过500px,而小于500px宽度的图片则按原大小显示。CSS2提供了max-width属性能够帮组我们实现这一目的。但很遗憾,挨千刀的IE6并不支持。
IE6一个弥补的办法就是通过注册img.onload事件,待图片加载完成后自动调整大小。以下代码取自著名的Discuz!论坛系统4.1版本对显示图片的处理。
<imgsrc="http://img8.imagepile.net/img8/47104p155.jpg"border="0"
onload="if(this.width>screen.width*0.7){this.resized=true;this.width=screen.width*0.7;
this.alt='ClickheretoopennewwindownCTRL+Mousewheeltozoomin/out';}"
onmouseover="if(this.width>screen.width*0.7){this.resized=true;this.width=screen.width*0.7;this.style.cursor='hand';this.alt='ClickheretoopennewwindownCTRL+Mousewheeltozoomin/out';}"
onclick="if(!this.resized){returntrue;}else{window.open('http://img8.imagepile.net/img8/47104p155.jpg');}"
onmousewheel="returnimgzoom(this);">
前文已述,浏览器并不保证事件处理函数执行。所以需要一个更稳定的方式跟踪图片加载过程,并执行设定的回调函数。
实现
image.complete属性标示的是图片加载状态,其值如果为ture,则表示加载成功。图片不存在或加载超时则值为false。利用setInterval()函数定时检查该状态则可以实现跟踪图片加载的状况。代码片断如下:
ImageLoader=Class.create();
ImageLoader.prototype={
initialize:function(options){
this.options=Object.extend({
timeout:60,//60s
onInit:Prototype.emptyFunction,
onLoad:Prototype.emptyFunction,
onError:Prototype.emptyFunction
},options||{});
this.images=[];
this.pe=newPeriodicalExecuter(this._load.bind(this),0.02);
},
........
}
利用Prototype的PeriodicalExecuter类创建一个定时器,每隔20毫秒检查一次图片的加载情况,并根据状态执行options参数中定义的回调函数。
使用
varloader=newImageLoader({
timeout:30,
onInit:function(img){
img.style.width='100px';
},
onLoad:function(img){
img.style.width='';
if(img.width>500)
img.style.width='500px';
},
onError:function(img){
img.src='error.jpg';//hintimage
}
});loader.loadImage(document.getElementsByTagName('img'));
上面的代码定义图片最初以100px显示,加载成功后如果图片实际宽度超过500px,则再强制定义为500px,否则显示原大小。如果图片不存在或加载超时(30秒为超时),则显示错误图片。
同理,可以应用ImageLoader的回调函数来根据需求自定义效果,例如默认显示loading,加载完成后再显示原图;图片首先灰度显示,加载完成后再恢复亮度等等。例如:
//needscriptaculouseffects.js
varloader=newImageLoader({
onInit:function(img){
Element.setOpacity(img,0.5);//默认5级透明
},
onLoad:function(img){
Effect.Appear(img);//恢复原图显示
}
});
附示例中包含完整的代码及使用pconline图片为例的测试,注意范例中使用了最新的Prototype1.5.0_rc1。
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0//EN">
<html>
<head>
<scriptsrc="prototype1.5.0_rc1.js"></script>
<scriptsrc="validation1.5.3/effects.js"></script>
</head>
<body>
<imgid="img0"src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169144171.jpg"/>
<imgid="img1"src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169158366.jpg"/>
<imgid="img2"src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169169983_mthumb.jpg"/>
<br/>加载失败测试<br/>
<imgid="img2"src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/000000000000.jpg"/>
<scripttype="text/javascript">
ImageLoader=Class.create();
ImageLoader.prototype={
initialize:function(options){
this.options=Object.extend({
timeout:60,//60s
onInit:Prototype.emptyFunction,
onLoad:Prototype.emptyFunction,
onError:Prototype.emptyFunction
},options||{});
this.images=[];
this.pe=newPeriodicalExecuter(this._load.bind(this),0.02);
},
loadImage:function(){
varself=this;
$A(arguments).each(function(img){
if(typeof(img)=='object')
$A(img).each(self._addImage.bind(self));
else
self._addImage(img);
});
},
_addImage:function(img){
img=$(img);
img.onerror=this._onerror.bind(this,img);
this.options.onInit.call(this,img);
if(this.options.timeout>0){
setTimeout(this._ontimeout.bind(this,img),this.options.timeout*1000);
}
this.images.push(img);
if(!this.pe.timer)
this.pe.registerCallback();
},
_load:function(){
this.images=this.images.select(this._onload.bind(this));
if(this.images.length==0){
this.pe.stop();
}
},
_checkComplete:function(img){
if(img._error){
returntrue;
}else{
returnimg.complete;
}
},
_onload:function(img){
if(this._checkComplete(img)){
this.options.onLoad.call(this,img);
img.onerror=null;
if(img._error)
try{deleteimg._error}catch(e){}
returnfalse;
}
returntrue;
},
_onerror:function(img){
img._error=true;
img.onerror=null;
this.options.onError.call(this,img);
},
_ontimeout:function(img){
if(!this._checkComplete(img)){
this._onerror(img);
}
}
}
varloader=newImageLoader({
timeout:30,
onInit:function(img){
img.style.width='100px';
},
onLoad:function(img){
img.style.width='';
if(img.width>500){
img.style.width='500px';
}
},
onError:function(img){
img.src='http://img.pconline.com.cn/nopic.gif';
}
});
loader.loadImage(document.getElementsByTagName('img'));
/*
varloader=newImageLoader({
timeout:30,
onInit:function(img){
Element.setOpacity(img,0.5);
},
onLoad:function(img){
Effect.Appear(img);
},
onError:function(img){
img.src='http://img.pconline.com.cn/nopic.gif';
}
});
*/
/*
$A(document.getElementsByTagName('img')).each(
function(img){
img.onload=function(){
img.style.width='300px';
}
}
);
*/
</script>
</body>
</html>