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 模板的应用示例
Nov 12 Javascript
jquery ajax跨域解决方法(json方式)
Feb 04 Javascript
5个最顶级jQuery图表类库插件【jquery插件库】
May 05 Javascript
vue2.0 与 bootstrap datetimepicker的结合使用实例
May 22 Javascript
Linux系统中利用node.js提取Word(doc/docx)及PDF文本的内容
Jun 17 Javascript
Angular.js组件之input mask对input输入进行格式化详解
Jul 10 Javascript
VueAwesomeSwiper在VUE中的使用以及遇到的一些问题
Jan 11 Javascript
详解node Async/Await 更好的异步编程解决方案
May 10 Javascript
微信小程序倒计时功能实例代码
Jul 17 Javascript
在Vue中获取组件声明时的name属性方法
Sep 12 Javascript
vue.js购物车添加商品组件的方法
Sep 17 Javascript
详解ES6中class的实现原理
Oct 03 Javascript
不错的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
阿拉伯的咖啡与水烟
2021/03/03 咖啡文化
Symfony生成二维码的方法
2016/02/04 PHP
PHP快速导出百万级数据到CSV或者EXCEL文件
2020/11/27 PHP
js几个验证函数代码
2010/03/25 Javascript
关于JavaScript命名空间的一些心得
2014/06/07 Javascript
Javascript 构造函数详解
2014/10/22 Javascript
在JavaScript中操作时间之getMonth()方法的使用
2015/06/10 Javascript
jQuery实现两款有动画功能的导航菜单代码
2015/09/16 Javascript
jQuery封装的屏幕居中提示信息代码
2016/06/08 Javascript
AngularJS中filter的使用实例详解
2017/08/25 Javascript
Angular之toDoList的实现代码示例
2017/12/02 Javascript
vue+iview 兼容IE11浏览器的实现方法
2019/01/07 Javascript
JavaScript变量提升和严格模式实例分析
2019/01/27 Javascript
javascript实现手动点赞效果
2019/04/09 Javascript
JavaScript模块管理的简单实现方式详解
2019/06/15 Javascript
layui.tree组件的使用以及搜索节点功能的实现
2019/09/26 Javascript
vue项目中使用多选框的实例代码
2020/07/22 Javascript
[01:43]倾听DOTA2英雄之声 魅惑魔女国服配音鉴赏
2013/06/06 DOTA
Python cookbook(数据结构与算法)将序列分解为单独变量的方法
2018/02/13 Python
python中正则表达式的使用方法
2018/02/25 Python
Django 浅谈根据配置生成SQL语句的问题
2018/05/29 Python
解决 jupyter notebook 回车换两行问题
2020/04/15 Python
Python 发送邮件方法总结
2020/08/10 Python
python之openpyxl模块的安装和基本用法(excel管理)
2021/02/03 Python
英国百安居装饰建材网上超市:B&Q
2016/09/13 全球购物
Manduka官网:瑜伽垫、瑜伽毛巾和服装
2018/07/02 全球购物
美国小蜜蜂Burt’s Bees德国官网:天然唇部、皮肤和身体护理产品
2020/06/14 全球购物
OnePlus加拿大官网:中国国际化手机品牌
2020/10/13 全球购物
医学院护理专业应届生求职信
2013/11/12 职场文书
设备管理实施方案
2014/05/31 职场文书
学校感恩节活动策划方案
2014/10/06 职场文书
井冈山红色之旅心得体会
2014/10/07 职场文书
2015年小学校长工作总结
2015/05/19 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
Python爬虫之自动爬取某车之家各车销售数据
2021/06/02 Python
十大最强飞行系宝可梦,BUG燕上榜,第二是飞行系王者
2022/03/18 日漫