注:2012.04.19更新,完善对IE的支持,修复onerror在IE下不执行问题。
有时候我们需要获取图片的尺寸,以用来对图片进行位置调整、大小限制等操作,以让图片适应页面的显示。传统的方法都是利用图片onload时获取尺寸,我也在介绍我自己用的限制图片尺寸文章中介绍了这种传统方法。但是最近我在《再谈javascript图片预加载技术》这里发现作者介绍了一种更好的方法,获取的速度远远大于传统的方法。
最近更新的社交媒体连接插件的“我的最新微博”功能支持了图片的显示(如我的右边栏),我在图片的浏览方式上加上了lightbox效果,所以我也在我的社交媒体连接插件中应用这种图片预加载技术。
实现原理
大家肯定都会见到这样一种现象,那就是浏览器在加载图片时,页面上会空出与这个图片尺寸一样的空白区域,这说明此刻图片的尺寸已经可以获取出来了,但是关键是onload必须是图片完整加载完毕才会触发。而在浏览器知道图片真正尺寸前,图片的尺寸都是固定的一个初始值,所以我们可以通过循环测试比较这个值的改变来判断浏览器是否已经获取了图片的真正尺寸。
关键代码
我在社交媒体连接插件中对作者的代码进行了些许修改,现在贴出我修改后的,仅供参考,与原作者无孰优孰劣之分。
我的主要修改是更改了回调函数的调用参数调用方式(改变了回调函数的上下文执行环境为img对象,可以通过this.width使用图片尺寸,并且可以对图片进行后续的调用处理)、以及将作者的setInterval改成了setTimeout方式、判断图片加载完成加入了onreadystatechange(因为ie下img节点使用onload有些问题)。
1: var imageReady=(function(){
2: var list=[],
3: timer=null,
4: prop=[['width','height'],['naturalWidth','naturalHeight']],
5: natural=Number(typeof document.createElement('img').naturalHeight==='number'),//是否支持HTML5新增的 naturalHeight
6: tick=function(){
7: for(var i=0;i<list.length;i++){
8: list[i].end?list.splice(i--,1):check.call(list[i],null);
9: }
10: list.length && (timer=setTimeout(tick,50)) || (timer=null);
11: },
12: /** overflow: 检测图片尺寸的改变
13: * img.__width,img.__height: 初载入时的尺寸
14: */
15: check=function(){
16: if(this[prop[natural][0]]!==this.__width || this[prop[natural][1]]!==this.__height || this[prop[natural][0]]*this[prop[natural][1]]>1024){
17: this.onready.call(this,null);
18: this.end=true;
19: }
20: };
21: return function(_img, onready, onload, onerror){
22: onready=onready || new Function();
23: onload=onload || new Function();
24: onerror=onerror || new Function();
25: var img=typeof _img=='string'?new Image():_img;
26: img.onerror=function(){// ie && ie<=8 的浏览器必须在src赋予前定义onerror
27: onerror.call(img,null);
28: img.end=true;
29: img=img.onload=img.onerror=img.onreadystatechange=null;
30: }
31: if(typeof _img=='string') img.src=_img;
32: if(!img)return;
33: if(img.complete){
34: onready.call(img,null);
35: onload.call(img,null);
36: return;
37: }
38: img.__width=img[prop[natural][0]];
39: img.__height=img[prop[natural][1]];
40: img.onready=onready;
41: check.call(img,null);
42: img.onload=img.onreadystatechange=function(){
43: if(img&&img.readyState&&img.readyState!='loaded'&&img.readyState!='complete'){return;}
44: !img.end && check.call(img,null);
45: onload.call(img,null);
46: img=img.onload=img.onerror=img.onreadystatechange=null;
47: }
48: if(!img.end){
49: list.push(img);
50: if(timer===null) timer=setTimeout(tick,50);
51: }
52: }
53: })();
54:
55:
56: /* 使用方式
57: * 以此图片节点为例:<img id="preload" src="http://wwww.qiqiboy.com/preload.jpg" alt="preload" />
58: */
59: imageReady(document.getElementById('preload'),function(){
60: console.log('onready');
61: },function(){
62: console.log('onload');
63: },function(){
64: console.log('onerror');
65: });
66:
67: /* 在调用函数中使用图片尺寸, 调用函数中的this指向preload图片对象 */
68: imageReady(document.getElementById('preload'),function(){
69: console.log('preload image width: '+this.width+'px, height: '+this.height+'px');
70: })
71:
72: /* 也可以直接使用url */
73: imageReady('http://wwww.qiqiboy.com/preload.jpg',function(){
74: console.log('preload image width: '+this.width+'px, height: '+this.height+'px');
75: })
demo还是看原作者的吧:点此
我会在社交媒体连接插件Social Medias Connect 的V1.5.6中将图片预加载方式替换为此种方式。
