天天看點

再談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;
		};
	};

};
           

可以看到上面必須等待圖檔加載完畢才能擷取尺寸,其速度不敢恭維,我們需要改進。

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
 * <a href="http://my.oschina.net/arthor" target="_blank" rel="external nofollow"  class="referer" target="_blank">@author</a> TangBin
 * <a href="http://my.oschina.net/see" target="_blank" rel="external nofollow"  class="referer" target="_blank">@see</a> 	http://www.planeart.cn/?p=1121
 * @param	{String}	圖檔路徑
 * @param	{Function}	尺寸就緒
 * @param	{Function}	加載完畢 (可選)
 * @param	{Function}	加載錯誤 (可選)
 * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
		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);
		};
	};
})();
           

調用例子:

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

版權聲明:本文為CSDN部落客「weixin_34304013」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/weixin_34304013/article/details/92065350