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 相关文章推荐
javascript动态添加、修改、删除对象的属性与方法详解
Jan 27 Javascript
jquery实现无限分级横向导航菜单的方法
Mar 12 Javascript
jQuery模拟黑客帝国矩阵效果实例
Jun 28 Javascript
jQuery+css实现的换页标签栏效果
Jan 27 Javascript
KnockoutJS 3.X API 第四章之事件event绑定
Oct 10 Javascript
jquery实现瀑布流效果 jquery下拉加载新数据
Dec 12 Javascript
JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】
Sep 26 Javascript
react同构实践之实现自己的同构模板
Mar 13 Javascript
vue.js中ref和$refs的使用及示例讲解
Aug 14 Javascript
jquery树形插件zTree高级使用详解
Aug 16 jQuery
Element中Slider滑块的具体使用
Jul 29 Javascript
vue-router 按需加载 component: () =&gt; import() 报错的解决
Sep 22 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
PHP学习之PHP变量
2006/10/09 PHP
windows下PHP APACHE MYSQ完整配置
2007/01/02 PHP
创建数据库php代码 用PHP写出自己的BLOG系统
2010/04/12 PHP
PHP列出MySQL中所有数据库的方法
2015/03/12 PHP
php实现文本数据导入SQL SERVER
2015/05/17 PHP
PHP中串行化用法示例
2016/11/16 PHP
PHP错误处理函数register_shutdown_function使用示例
2017/07/03 PHP
jQuery toggle()设置CSS样式
2009/11/05 Javascript
理解Javascript_02_理解undefined和null
2010/10/11 Javascript
js模拟点击事件实现代码
2012/11/06 Javascript
JS下拉框内容左右移动效果的具体实现
2013/07/10 Javascript
jquery的live使用注意事项
2014/02/18 Javascript
页面内容排序插件jSort使用方法
2015/10/10 Javascript
ES6新特性八:async函数用法实例详解
2017/04/21 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
iconfont的三种使用方式详解
2018/08/05 Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
2019/04/17 Javascript
ant design vue 表格table 默认勾选几项的操作
2020/10/31 Javascript
uni-app 自定义底部导航栏的实现
2020/12/11 Javascript
Python3.5局部变量与全局变量作用域实例分析
2019/04/30 Python
python 画出使用分类器得到的决策边界
2019/08/21 Python
python tkinter之顶层菜单、弹出菜单实例
2020/03/04 Python
Python中使用threading.Event协调线程的运行详解
2020/05/02 Python
使用scrapy ImagesPipeline爬取图片资源的示例代码
2020/09/28 Python
Python-openpyxl表格读取写入的案例详解
2020/11/02 Python
Uber Eats台湾:寻找附近提供送餐服务的餐厅
2018/05/07 全球购物
android面试问题与答案
2016/12/27 面试题
Linux Interview Questions For software testers
2012/06/02 面试题
商场活动策划方案
2014/01/24 职场文书
护理学应聘自荐书范文
2014/02/05 职场文书
保健品市场营销方案
2014/03/31 职场文书
水利水电建筑施工应届生求职信
2014/07/04 职场文书
学校四风对照检查材料
2014/08/28 职场文书
2019XX公司员工考核管理制度!
2019/08/07 职场文书
《家庭教育》读后感3篇
2019/12/18 职场文书
上个世纪50年代的可穿戴技术:无线电帽子
2022/02/18 无线电