【 tulaoshi.com - Web开发 】
                             
                            目的 
在图片的加载过程中,提供定义图片加载成功或加载失败/超时时的回调函数,并确保执行。  
动机 
原生JavaScript已经对 Image 对象提供了 onload 和 onerror 注册事件。但在浏览器缓存及其他因素的影响下,用户在使用回退按钮或者刷新页面时 onload 事件常常未能稳定触发。在我开发的相册系统中,我希望图片能根据自定义的大小显示以免导致页面变形,例如最宽不得超过500px,而小于500px宽度的图片则按原大小显示。CSS2 提供了 max-width 属性能够帮组我们实现这一目的。但很遗憾,挨千刀的IE6并不支持。  
IE6一个弥补的办法就是通过注册 img.onload 事件,待图片加载完成后自动调整大小。以下代码取自著名的Discuz!论坛系统4.1版本对显示图片的处理。  
img src="http://img8.imagepile.net/img8/47104p155.jpg" border="0" 
onload="if(this.widthscreen.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.widthscreen.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。 
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 //EN" 
html 
head 
script src="prototype1.5.0_rc1.js"/script 
script src="validation1.5.3/effects.js"/script 
/head 
body 
img id="img0" src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169144171.jpg" / 
img id="img1" src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169158366.jpg" / 
img id="img2" src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/1158169169983_mthumb.jpg" / 
br /加载失败测试br / 
img id="img2" src="http://img.pconline.com.cn/images/photoblog/2026024/20069/14/000000000000.jpg" / 
script type="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 = new PeriodicalExecuter(this._load.bind(this), 0.02); 
  }, 
  loadImage : function() { 
    var self = 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) { 
      return true; 
    } else { 
      return img.complete; 
    } 
  }, 
  _onload : function(img) { 
    if (this._checkComplete(img)) { 
      this.options.onLoad.call(this, img); 
      img.onerror = null; 
      if (img._error) 
        try {delete img._error}catch(e){} 
      return false; 
    } 
    return true; 
  }, 
  _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); 
    } 
  } 
} 
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 = 'http://img.pconline.com.cn/nopic.gif'; 
  } 
}); 
loader.loadImage(document.getElementsByTagName('img')); 
/* 
var loader = new ImageLoader({ 
  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