比onload更快擷取圖片尺寸
文章更新:2011-05-31
lightbox類效果為了讓圖片置中顯示而使用預先載入,需要等待完全載入完畢才能顯示,體驗不佳(如filick相簿的全屏效果)。javascript無法擷取img檔案頭資料,真的是這樣嗎?本文通過一個巧妙的方法讓javascript擷取它。
這是大部分人使用預先載入擷取圖片大小的例子:
var imgLoad = function (url, callback) {02 var img = new Image();03 04 img.src = url;05 if (img.complete) {06 callback(img.width, img.height);07 } else {08 img.onload = function () {09 callback(img.width, img.height);10 img.onload = null;11 };12 };13 14};
可以看到上面必須等待圖片載入完畢才能擷取尺寸,其速度不敢恭維,我們需要改進。
web應用程式區別於傳統型應用程式,響應速度才是最好的使用者體驗。如果想要速度與優雅兼得,那就必須提前獲得圖片尺寸,如何在圖片沒有載入完畢就能擷取圖片尺寸?
十多年的上網經驗告訴我:瀏覽器在載入圖片的時候你會看到圖片會先佔用一塊地然後才慢慢載入完畢,並且不需要預設width與height屬性,因為瀏覽器能夠擷取圖片的頭部資料。基於此,只需要使用javascript定時偵測圖片的尺寸狀態便可得知圖片尺寸就緒的狀態。
當然實際中會有一些相容陷阱,如width與height檢測各個瀏覽器的不一致,還有webkit new Image()建立的圖片會受以處在載入進程中同url圖片影響,經過反覆測試後的最佳處理方式:
// 更新:02// 05.27: 1、保證回調執行順序:error > ready > load;2、回呼函數this指向img本身03// 04-02: 1、增加圖片完全載入後的回調 2、提高效能04 05/**06 * 圖標題資料載入就緒事件 - 更快擷取圖片尺寸07 * @version 2011.05.2708 * <a href="http://my.oschina.net/arthor" class="referer" target="_blank">@author</a> TangBin09 * <a href="http://my.oschina.net/see" class="referer" target="_blank">@see</a> http://www.planeart.cn/?p=112110 * @param {String} 圖片路徑11 * @param {Function} 尺寸就緒12 * @param {Function} 載入完畢 (可選)13 * @param {Function} 載入錯誤 (可選)14 * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {15 alert('size ready: width=' + this.width + '; height=' + this.height);16 });17 */18var imgReady = (function () {19 var list = [], intervalId = null,20 21 // 用來執行隊列22 tick = function () {23 var i = 0;24 for (; i < list.length; i++) {25 list[i].end ? list.splice(i--, 1) : list[i]();26 };27 !list.length && stop();28 },29 30 // 停止所有定時器隊列31 stop = function () {32 clearInterval(intervalId);33 intervalId = null;34 };35 36 return function (url, ready, load, error) {37 var onready, width, height, newWidth, newHeight,38 img = new Image();39 40 img.src = url;41 42 // 如果圖片被緩衝,則直接返回快取資料43 if (img.complete) {44 ready.call(img);45 load && load.call(img);46 return;47 };48 49 width = img.width;50 height = img.height;51 52 // 載入錯誤後的事件53 img.onerror = function () {54 error && error.call(img);55 onready.end = true;56 img = img.onload = img.onerror = null;57 };58 59 // 圖片尺寸就緒60 onready = function () {61 newWidth = img.width;62 newHeight = img.height;63 if (newWidth !== width || newHeight !== height ||64 // 如果圖片已經在其他地方載入可使用面積檢測65 newWidth * newHeight > 102466 ) {67 ready.call(img);68 onready.end = true;69 };70 };71 onready();72 73 // 完全載入完畢的事件74 img.onload = function () {75 // onload在定時器時間差範圍內可能比onready快76 // 這裡進行檢查並保證onready優先執行77 !onready.end && onready();78 79 load && load.call(img);80 81 // IE gif動畫會迴圈執行onload,置空onload即可82 img = img.onload = img.onerror = null;83 };84 85 // 排入佇列中定期執行86 if (!onready.end) {87 list.push(onready);88 // 無論何時只允許出現一個定時器,減少瀏覽器效能損耗89 if (intervalId === null) intervalId = setInterval(tick, 40);90 };91 };92})();
調用例子:
imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {2 alert('size ready: width=' + this.width + '; height=' + this.height);3});
是不是很簡單?這樣的方式擷取攝影層級照片尺寸的速度往往是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