DIY 自己搞定圖片延遲下載

所提供的程式碼範例是以 WordPress 系統為基礎來製作的,但這製作的觀念其實移轉到任何系統都可行的。

雖然已經有很多優質的外掛當中會有包含到這一個功能,但個人試用過幾個之後發現對我的網站都會有一點點的小問題,所以最後決定還是自己寫來達到我希望的效果。

在理想請況下,圖片延遲的功能應該在載入圖片後還在可視範圍內的圖片會被下載。

歡樂夢想國這一篇文章為範例,理想是進入網頁後只有前兩張圖片會被使用者下載,也就是他們 LOGO 跟迎賓木偶兩張。但是在 Jetpack 的圖片延遲功能作用下,其實你還是會一次將所有的圖片下載。

這是因為當使用者進入網站後,在所有的圖片都未下載的情況下,整篇文章都在畫面的可視範圍之內,類似下圖那樣。
所以 Jetpack 的圖片延遲下載的功能,就會判定文章中的每一張圖片都是需要下載的,可是如果你連過去會發現只有前兩張圖片是看的到的(以螢幕解析度 1920 x 1080 為例 )。

所以最後本網站使用的就是我自己寫的來達成圖片延遲載入的功能。
而會寫成這篇文章,當然就是因為我不是用外掛的形式來達成,而是寫在佈景主題當中。

首先開啟佈景主題的 functions.php 檔案,在最後面加入

add_action('wp_loaded', 'ry_diy_lazy_image');
function ry_diy_lazy_image() {
	if( is_admin() ) {
		return ;
	}
	if( wp_doing_ajax() ) {
		return ;
	}

	ob_start(function($buffer) {
		$buffer = preg_replace_callback('#<img([^>]+?)(>(.*?)</\1>|[/]?>)#si', function($matches) {
			$matches[1] = preg_replace('/ src=(["|'])/i', ' data-src=$1', $matches[1]);
			$matches[1] = preg_replace('/ srcset=(["|'])/i', ' data-srcset=$1', $matches[1]);
			if( strpos($matches[1], ' class=') === FALSE ) {
				$matches[1] .= ' class="need-load-img"';
			} else {
				$matches[1] = preg_replace('/ class=(["|'])/i', ' class=$1need-load-img ', $matches[1]);
			}

			return '<img' . $matches[1] . '><noscript>' . $matches[0] . '</noscript>';
		}, $buffer);

		return $buffer;
	});
}

add_action('wp_enqueue_scripts', 'ry_diy_lazy_image_script');
function ry_diy_lazy_image_script() {
	wp_enqueue_script('ry_diy_lazy_image', get_template_directory_uri(). '/js/lazy_image.js', array('jquery'), false, true);	 
}
add_action('wp_loaded', 'ry_diy_lazy_image');
function ry_diy_lazy_image() {
	if( is_admin() ) {
		return ;
	}
	if( wp_doing_ajax() ) {
		return ;
	}

	ob_start(function($buffer) {
		$buffer = preg_replace_callback('#<img([^>]+?)(>(.*?)</\1>|[/]?>)#si', function($matches) {
			$matches[1] = preg_replace('/ src=(["|'])/i', ' data-src=$1', $matches[1]);
			$matches[1] = preg_replace('/ srcset=(["|'])/i', ' data-srcset=$1', $matches[1]);
			if( strpos($matches[1], ' class=') === FALSE ) {
				$matches[1] .= ' class="need-load-img"';
			} else {
				$matches[1] = preg_replace('/ class=(["|'])/i', ' class=$1need-load-img ', $matches[1]);
			}

			return '<img' . $matches[1] . '><noscript>' . $matches[0] . '</noscript>';
		}, $buffer);

		return $buffer;
	});
}

add_action('wp_enqueue_scripts', 'ry_diy_lazy_image_script');
function ry_diy_lazy_image_script() {
	wp_enqueue_script('ry_diy_lazy_image', get_template_directory_uri(). '/js/lazy_image.js', array('jquery'), false, true);	 
}

接下來在你的佈景主題下新增 js 資料夾,然後在其中加入 lazy_image.js 檔案內容為

jQuery(document).ready(function($) {

$('img').on('showimg', function() {
	var $this = $(this);
	if( $this.attr('data-src') !== undefined ) {
		$this.removeClass('need-load-img')
			.attr('src', $this.data('src'))
			.attr('srcset', $this.data('srcset'))
			.removeAttr('data-src')
			.removeAttr('data-srcset');
	}
});

$(window).scroll(tryLoadImage).trigger('scroll');
function tryLoadImage() {
	var imgList = {};
	$('.need-load-img').each(function(){
		var $this = $(this),
			offsetTop = $this.offset().top;

		if( offsetTop < (document.documentElement.scrollTop + document.documentElement.clientHeight) ) {
			if( imgList[offsetTop] === undefined ) {
				imgList[offsetTop] = [];
			}
			imgList[offsetTop].push($this);
		}
	});

	var imgListKeys = Object.keys(imgList);
	imgListKeys.sort();

	for( var i = 0; i < 3; ++i) {
		if( imgListKeys[i] !== undefined ) {
			imgList[imgListKeys[i]].forEach(function($img) {
				$img.trigger('showimg');
			});
		}
	}
	if( imgListKeys[i] !== undefined ) {
		setTimeout(tryLoadImage, 300);
	}
}

});

最後不負責任說明,以上程式碼為從我的佈景主題當中擷取並適度調整而來的,並未完整測試。
所以不保證在所有情況下都可以正常運作。

創用 CC 授權條款
本篇文章採用 創用 CC 姓名標示-非商業性-相同方式分享 4.0 國際 授權條款 授權。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *