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作用域你想知道的一切
Feb 04 Javascript
bootstrap datepicker 与bootstrapValidator同时使用时选择日期后无法正常触发校验的解决思路
Sep 28 Javascript
深入学习 JavaScript中的函数调用
Mar 23 Javascript
Vue中定义全局变量与常量的各种方式详解
Aug 23 Javascript
JS跳转手机站url的若干注意事项
Oct 18 Javascript
JavaScript获取移动设备型号的实现代码(JS获取手机型号和系统)
Mar 10 Javascript
详解react内联样式使用webpack将px转rem
Sep 13 Javascript
es6基础学习之解构赋值
Dec 10 Javascript
nuxt引入组件和公共样式的操作
Nov 05 Javascript
聊聊vue 中的v-on参数问题
Jan 29 Vue.js
5种方法告诉你如何使JavaScript 代码库更干净
Sep 15 Javascript
用JS创建一个录屏功能
Nov 11 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
Windows下安装Memcached的步骤说明
2010/04/25 PHP
PHP中func_get_args(),func_get_arg(),func_num_args()的区别
2013/09/30 PHP
PHP pear安装配置教程
2016/05/14 PHP
PHP 并发场景的几种解决方案
2019/06/14 PHP
加载jQuery后$冲突的解决办法
2010/07/09 Javascript
js格式化货币数据实现代码
2013/09/04 Javascript
JQuery给网页更换皮肤的方法
2015/05/30 Javascript
jquery+css实现动感的图片切换效果
2015/11/25 Javascript
Javascript编程中几种继承方式比较分析
2015/11/28 Javascript
js+css简单实现网页换肤效果
2015/12/29 Javascript
js中遍历Map对象的简单实例
2016/08/08 Javascript
AngularJS模块详解及示例代码
2016/08/17 Javascript
easyui-edatagrid.js实现回车键结束编辑功能的实例
2017/04/12 Javascript
Bootstrap进度条与AJAX后端数据传递结合使用实例详解
2017/04/23 Javascript
jQuery插件select2利用ajax高效查询大数据列表(可搜索、可分页)
2017/05/19 jQuery
JavaScript用二分法查找数据的实例代码
2017/06/17 Javascript
vue2.0 根据状态值进行样式的改变展示方法
2018/03/13 Javascript
JQuery扩展对象方法操作示例
2018/08/21 jQuery
vue移动端监听滚动条高度的实现方法
2018/09/03 Javascript
JS实现字体背景跑马灯
2020/01/06 Javascript
微信小程序实现发微博功能的示例代码
2020/06/24 Javascript
[45:38]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第一局
2016/03/02 DOTA
Ruby元编程基础学习笔记整理
2016/07/02 Python
详解python 发送邮件实例代码
2016/12/22 Python
微信跳一跳python辅助软件思路及图像识别源码解析
2018/01/04 Python
Python面向对象类的继承实例详解
2018/06/27 Python
Python实现操纵控制windows注册表的方法分析
2019/05/24 Python
vscode 配置 python3开发环境的方法
2019/09/19 Python
开启Django博客的RSS功能的实现方法
2020/02/17 Python
阿迪达斯比利时官方商城:adidas比利时
2016/10/10 全球购物
澳大利亚宠物食品和药物在线:Jumbo Pets
2018/03/24 全球购物
蒙蒂塞罗商店:Monticello Shop
2018/11/25 全球购物
Java如何格式化日期
2012/08/07 面试题
旷工检讨书1000字
2015/01/01 职场文书
办公室日常管理制度
2015/08/04 职场文书
SQL Server代理:理解SQL代理错误日志处理方法
2021/06/30 SQL Server