目的
在图片的加载过程中,提供定义图片加载成功或加载失败/超时时的回调函数,并确保执行。
动机
原生JavaScript已经对 Image 对象提供了 onload 和 onerror 注册事件。但在浏览器缓存及其他因素的影响下,用户在使用回退按钮或者刷新页面时 onload 事件常常未能稳定触发。在我开发的相册系统中,我希望图片能根据自定义的大小显示以免导致页面变形,例如最宽不得超过500px,而小于500px宽度的图片则按原大小显示。CSS2 提供了 max-width 属性能够帮组我们实现这一目的。但很遗憾,挨千刀的IE6并不支持。
IE6一个弥补的办法就是通过注册 img.onload 事件,待图片加载完成后自动调整大小。以下代码取自著名的Discuz!论坛系统4.1版本对显示图片的处理。
onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.alt='Click here to open new windownCTRL+Mouse wheel to zoom in/out';}"
onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7; this.style.cursor='hand'; this.alt='Click here to open new windownCTRL+Mouse wheel to zoom in/out';}"
onclick="if(!this.resized) {return true;} else {window.open('http://img8.imagepile.net/img8/47104p155.jpg');}"
onmousewheel="return imgzoom(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 = new PeriodicalExecuter(this._load.bind(this), 0.02);
},
........
}
利用Prototype 的PeriodicalExecuter类创建一个定时器,每隔20毫秒检查一次图片的加载情况,并根据状态执行 options 参数中定义的回调函数。
使用
var loader = new ImageLoader({
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'; //hint image
}
});loader.loadImage(document.getElementsByTagName('img'));
上面的代码定义图片最初以100px显示,加载成功后如果图片实际宽度超过500px,则再强制定义为500px,否则显示原大小。如果图片不存在或加载超时(30秒为超时),则显示错误图片。
同理,可以应用 ImageLoader 的回调函数来根据需求自定义效果,例如默认显示loading,加载完成后再显示原图;图片首先灰度显示,加载完成后再恢复亮度等等。例如:
//need scriptaculous effects.js
var loader = new ImageLoader({
onInit: function(img) {
Element.setOpacity(img, 0.5); //默认5级透明
},
onLoad: function(img) {
Effect.Appear(img); //恢复原图显示
}
});
附示例中包含完整的代码及使用pconline图片为例的测试, 注意 范例中使用了最新的Prototype 1.5.0_rc1。
nbsp;HTML PUBLIC "-//W3C//DTD HTML 4.0 //EN">
<script></script>
<script></script>
加载失败测试
<script> <BR>ImageLoader = Class.create(); <BR>ImageLoader.prototype = { <br><br> initialize : function(options) { <BR> this.options = Object.extend({ <BR> timeout: 60, //60s <BR> onInit: Prototype.emptyFunction, <BR> onLoad: Prototype.emptyFunction, <BR> onError: Prototype.emptyFunction <BR> }, options || {}); <BR> this.images = []; <BR> this.pe = new PeriodicalExecuter(this._load.bind(this), 0.02); <BR> }, <br><br> loadImage : function() { <BR> var self = this; <BR> $A(arguments).each(function(img) { <BR> if (typeof(img) == 'object') <BR> $A(img).each(self._addImage.bind(self)); <BR> else <BR> self._addImage(img); <BR> }); <BR> }, <br><br> _addImage : function(img) { <BR> img = $(img); <BR> img.onerror = this._onerror.bind(this, img); <BR> this.options.onInit.call(this, img); <BR> if (this.options.timeout > 0) { <BR> setTimeout(this._ontimeout.bind(this, img), this.options.timeout*1000); <BR> } <BR> this.images.push(img); <BR> if (!this.pe.timer) <BR> this.pe.registerCallback(); <BR> }, <br><br> <BR> _load: function() { <BR> this.images = this.images.select(this._onload.bind(this)); <BR> if (this.images.length == 0) { <BR> this.pe.stop(); <BR> } <BR> }, <br><br> _checkComplete : function(img) { <BR> if (img._error) { <BR> return true; <BR> } else { <BR> return img.complete; <BR> } <BR> }, <br><br> _onload : function(img) { <BR> if (this._checkComplete(img)) { <BR> this.options.onLoad.call(this, img); <BR> img.onerror = null; <BR> if (img._error) <BR> try {delete img._error}catch(e){} <BR> return false; <BR> } <BR> return true; <BR> }, <br><br> _onerror : function(img) { <BR> img._error = true; <BR> img.onerror = null; <BR> this.options.onError.call(this, img); <BR> }, <br><br> _ontimeout : function(img) { <BR> if (!this._checkComplete(img)) { <BR> this._onerror(img); <BR> } <BR> } <br><br>} <br><br>var loader = new ImageLoader({ <BR> timeout: 30, <BR> onInit: function(img) { <BR> img.style.width = '100px'; <BR> }, <BR> onLoad: function(img) { <BR> img.style.width = ''; <BR> if (img.width > 500) { <BR> img.style.width = '500px'; <BR> } <BR> }, <BR> onError: function(img) { <BR> img.src = 'http://img.pconline.com.cn/nopic.gif'; <BR> } <BR>}); <br><br>loader.loadImage(document.getElementsByTagName('img')); <br><br>/* <BR>var loader = new ImageLoader({ <BR> timeout: 30, <BR> onInit: function(img) { <BR> Element.setOpacity(img, 0.5); <BR> }, <BR> onLoad: function(img) { <BR> Effect.Appear(img); <BR> }, <BR> onError: function(img) { <BR> img.src = 'http://img.pconline.com.cn/nopic.gif'; <BR> } <BR>}); <BR>*/ <br><br>/* <BR>$A(document.getElementsByTagName('img')).each( <BR>function(img) { <BR> img.onload = function() { <BR> img.style.width = '300px'; <BR> } <BR>} <BR>); <BR>*/ <br><br></script>