Track Image Loading效果代码分析


Posted in Javascript onAugust 13, 2007

目的
在图片的加载过程中,提供定义图片加载成功或加载失败/超时时的回调函数,并确保执行。 

动机
原生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.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。

<!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>

Javascript 相关文章推荐
jquery 选择器引擎sizzle浅析
Feb 06 Javascript
javascript 实现子父窗体互相传值的简单实例
Feb 17 Javascript
js调试系列 源码定位与调试[基础篇]
Jun 18 Javascript
JQuery 的跨域方法推荐_可跨任何网站
May 18 Javascript
基于angularjs实现图片放大镜效果
Aug 31 Javascript
jQuery简单实现列表隐藏和显示效果示例
Sep 12 Javascript
EasyUI创建人员树的实例代码
Sep 15 Javascript
微信小程序request请求后台接口php的实例详解
Sep 20 Javascript
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
Mar 06 Javascript
layui使用button按钮 点击出现弹层 弹层中加载表单的实例
Sep 04 Javascript
Vue Router中应用中间件的方法
Aug 06 Javascript
Vue $attrs &amp; inheritAttr实现button禁用效果案例
Dec 07 Vue.js
不错的JS中变量相关的细节分析
Aug 13 #Javascript
javascript-TreeView父子联动效果保持节点状态一致
Aug 12 #Javascript
TopList标签和JavaScript结合两例
Aug 12 #Javascript
Javascript-Mozilla和IE中的一个函数直接量的问题分析
Aug 12 #Javascript
IE和Mozilla的兼容性汇总event
Aug 12 #Javascript
收藏Javascript中常用的55个经典技巧
Aug 12 #Javascript
JavaScript-世界上误解最深的语言分析
Aug 12 #Javascript
You might like
关于PHP中Session文件过多的问题及session文件保存位置
2016/03/17 PHP
在CentOS系统上从零开始搭建WordPress博客的全流程记录
2016/04/21 PHP
共享自己写一个框架DreamScript
2007/01/20 Javascript
jquery 圆形旋转图片滚动切换效果
2011/01/19 Javascript
可在线编辑网页文字效果代码(单击)
2013/03/02 Javascript
简单实用的全选反选按钮例子
2013/10/18 Javascript
a标签的href与onclick事件的区别详解
2014/11/12 Javascript
jQuery实现仿腾讯视频列表分页效果的方法
2015/08/07 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
2016/04/05 Javascript
JS 在数组指定位置插入/删除数据的方法
2017/01/12 Javascript
原生JS实现垂直手风琴效果
2017/02/19 Javascript
Angular1.x自定义指令实例详解
2017/03/01 Javascript
js原生Ajax的封装和原理详解
2017/03/11 Javascript
Vue 2.0+Vue-router构建一个简单的单页应用(附源码)
2017/03/14 Javascript
从零开始学习Node.js系列教程四:多页面实现的数学运算示例
2017/04/13 Javascript
Nodejs实现多房间简易聊天室功能
2017/06/20 NodeJs
详解django模板与vue.js冲突问题
2019/07/07 Javascript
axios实现文件上传并获取进度
2020/03/25 Javascript
Js代码中的span拼接问题解决
2019/11/22 Javascript
Python函数学习笔记
2008/10/07 Python
Django中处理出错页面的方法
2015/07/15 Python
使用Python判断质数(素数)的简单方法讲解
2016/05/05 Python
Python设计模式之门面模式简单示例
2018/01/09 Python
用pycharm开发django项目示例代码
2018/10/24 Python
pyqt5之将textBrowser的内容写入txt文档的方法
2019/06/21 Python
Python爬虫实现“盗取”微信好友信息的方法分析
2019/09/16 Python
Python使用微信接入图灵机器人过程解析
2019/11/04 Python
python实现逻辑回归的示例
2020/10/09 Python
python 基于opencv 实现一个鼠标绘图小程序
2020/12/11 Python
总经理秘书岗位职责
2014/03/17 职场文书
“四风”问题的主要表现和危害思想汇报
2014/09/19 职场文书
开展批评与自我批评发言材料
2014/10/17 职场文书
医生见习报告范文
2014/11/03 职场文书
优秀教师申报材料
2014/12/16 职场文书
2015年仓库管理工作总结
2015/05/25 职场文书
运动会观后感
2015/06/09 职场文书