談JavaScript圖片預加載技術
比onload更快獲取圖片尺寸
文章更新:2011-05-31
lightbox類效果為了讓圖片居中顯示而使用預加載,需要等待完全加載完畢才能顯示,體驗不佳(如filick相冊的全屏效果)。javascript無法獲取img文件頭數據,真的是這樣嗎?本文通過一個巧妙的方法讓javascript獲取它。
這是大部分人使用預加載獲取圖片大小的例子:
var imgLoad = function (url, callback) { var img = new Image();img.src = url; if (img.complete) { callback(img.width, img.height); } else { img.onload = function () { callback(img.width, img.height); img.onload = null; }; };
};</pre>
可以看到上面必須等待圖片加載完畢才能獲取尺寸,其速度不敢恭維,我們需要改進。
web應用程序區別于桌面應用程序,響應速度才是最好的用戶體驗。如果想要速度與優雅兼得,那就必須提前獲得圖片尺寸,如何在圖片沒有加載完畢就能獲取圖片尺寸?
十多年的上網經驗告訴我:瀏覽器在加載圖片的時候你會看到圖片會先占用一塊地然后才慢慢加載完畢,并且不需要預設width與height屬性,因為瀏覽器能夠獲取圖片的頭部數據。基于此,只需要使用javascript定時偵測圖片的尺寸狀態便可得知圖片尺寸就緒的狀態。
當然實際中會有一些兼容陷阱,如width與height檢測各個瀏覽器的不一致,還有webkit new Image()建立的圖片會受以處在加載進程中同url圖片影響,經過反復測試后的最佳處理方式:
// 更新: // 05.27: 1、保證回調執行順序:error > ready > load;2、回調函數this指向img本身 // 04-02: 1、增加圖片完全加載后的回調 2、提高性能/**
- 圖片頭數據加載就緒事件 - 更快獲取圖片尺寸
- @version 2011.05.27
- @author TangBin
- @see http://www.planeart.cn/?p=1121
- @param {String} 圖片路徑
- @param {Function} 尺寸就緒
- @param {Function} 加載完畢 (可選)
- @param {Function} 加載錯誤 (可選)
@example imgReady('
alert('size ready: width=' + this.width + '; height=' + this.height);
}); */ var imgReady = (function () { var list = [], intervalId = null,
// 用來執行隊列 tick = function () {
var i = 0; for (; i < list.length; i++) { list[i].end ? list.splice(i--, 1) : list[i](); }; !list.length && stop();
},
// 停止所有定時器隊列 stop = function () {
clearInterval(intervalId); intervalId = null;
};
return function (url, ready, load, error) {
var onready, width, height, newWidth, newHeight, img = new Image(); img.src = url; // 如果圖片被緩存,則直接返回緩存數據 if (img.complete) { ready.call(img); load && load.call(img); return; }; width = img.width; height = img.height; // 加載錯誤后的事件 img.onerror = function () { error && error.call(img); onready.end = true; img = img.onload = img.onerror = null; }; // 圖片尺寸就緒 onready = function () { newWidth = img.width; newHeight = img.height; if (newWidth !== width || newHeight !== height || // 如果圖片已經在其他地方加載可使用面積檢測 newWidth * newHeight > 1024 ) { ready.call(img); onready.end = true; }; }; onready(); // 完全加載完畢的事件 img.onload = function () { // onload在定時器時間差范圍內可能比onready快 // 這里進行檢查并保證onready優先執行 !onready.end && onready(); load && load.call(img); // IE gif動畫會循環執行onload,置空onload即可 img = img.onload = img.onerror = null; }; // 加入隊列中定期執行 if (!onready.end) { list.push(onready); // 無論何時只允許出現一個定時器,減少瀏覽器性能損耗 if (intervalId === null) intervalId = setInterval(tick, 40); };
}; })();</pre>
調用例子:imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () { alert('size ready: width=' + this.width + '; height=' + this.height); });
是不是很簡單?這樣的方式獲取攝影級別照片尺寸的速度往往是onload方式的幾十多倍,而對于web普通(800×600內)瀏覽級別的圖片能達到秒殺效果。看了這個再回憶一下你見過的web相冊,是否絕大部分都可以重構一下呢?好了,請觀賞令人愉悅的 DEMO :
http://www.planeart.cn/demo/imgReady/
(通過測試的瀏覽器:Chrome、Firefox、Safari、Opera、IE6、IE7、IE8)
planeArt.cn原創文章,原文地址:http://www.planeart.cn/?p=1121