通常Webページは読み込む時に画像をすべてダウンロードしてからページが表示されます。 まだ画面の領域に入っていない隠れている部分も含めて画像がどれだけあろうが全ての画像を読み込んでしまいます。 そうすると当然、ページのリクエストや転送量が増えてページ表示まで時間がかかりユーザーを待たせることになります。これを改善するのが「画像の遅延読み込み」です。 画面をスクロールして表示領域に入った画像だけを読み込ませることで、必要なタイミングで必要な分だけ画像が表示されるので無駄な読み込みが発生せずにページが軽くなります。
画像の多いサイトでページの高速化に取り組む時に最も効果が出やすい手法です。
画像の遅延読み込みライブラリで、以下のような特徴があります。
picture
/srcset
)3パターンありますが、環境に合わせてどうぞ。
Githubからファイルをダウンロードしてlazysizes.min.js
を読み込ませる。
<script src="assets/js/lazysizes.min.js"></script>
CDNを読み込ませる場合は、こちらで各バージョンやプラグインを指定してコピペします。
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js"></script>
Yarn もしくは npm でインストールして使う場合は以下。
// yarn
yarn add -D lazysizes
// npm
npm install -D lazysizes
インストールしたらJSでimportして読み込ませます。
import 'lazysizes';
遅延読み込みを適用したい画像の<img>
タグのclassにlazyload
を、src属性にはダミー画像を、data-src属性に表示させる画像を設定します。
<img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7"
data-src="assets/images/lazyload.png"
alt="">
ここでダミーに使っているdata:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7
というURLはbase64というデータ形式で1×1pxの透過gif画像を直接HTMLに文字列で埋め込んでいます。これなら外部ファイルとしてリクエストは発生しませんし、容量も最小限で済みます。ダミーは自分で用意したものでも構いませんが、サイズが大きくなると遅延読み込みが無意味になるのでご注意ください。
ダミーについてはこちらから持ってきました。
1x1の最小GIF・PNGファイル(透過/base64)
https://qiita.com/CloudRemix/items/92e68a048a0da93ed240
基本的な使い方は以上です!
これだけで遅延読み込みが実現できます。
DEMO
※サーバーが非力なので503エラーで画像が表示されない場合があります。すみません・・・
ちなみにiframeの場合は、ダミー画像が必要ないです。
<iframe class="lazyload" data-src="GoogleMapなど表示させたいURL"></iframe>
unveilhooks
プラグインを使えば背景画像やscript
タグも遅延ロードできます。
//lazysizesを読み込む
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js"></script>
//ライブラリの直後にプラグインを読み込ませる
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/plugins/unveilhooks/ls.unveilhooks.min.js">
Githubからダウンロードした場合はplugins/unveilhooks
ディレクトリ内のls.unveilhooks.min.js
をコピーして使えばOKです。
<!-- 背景 -->
<div class="lazyload" data-bg="bg.jpg"></div>
<!-- JSなどのscriptタグ -->
<div class="lazyload" data-script="module-name.js"></div>
<!--CSSタグなどのlinkタグ -->
<div class="lazyload" data-link="my-style.css"></div>
<!-- videoタグ -->
<video class="lazyload" data-poster="poster.jpg" preload="none"></video>
<!-- CSSとJS -->
<div class="lazyload" data-script="my-script.js" data-link="my-style.css"></div>
サンプルソースは公式からお借りしました。
lazysizes unveilhooks extension
https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/unveilhooks
スマホとPCなどデバイスに合わせて最適な画像を出し分ける手法ですね。<picture>
要素を使いメディアクエリで出し分けるのが最近の主流だと思いますが、そちらにも対応しているのがlazysizesの特徴でもあります。
<picture>
<source
media="(min-width:1440px)"
data-srcset="assets/images/lazy-pc@2x.jpg">
<source
media="(min-width:768px)"
data-srcset="assets/images/lazy-pc.jpg 1x, assets/images/lazy-pc@2x.jpg 2x">
<img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7"
data-src="assets/images/lazy-sp.jpg"
alt="">
</picture>
img
タグは通常と同じように変更します。
あとはpicture
要素で画像を出し分ける時に使うsource
タグのsrcset
属性をdata-srcset
に変えてやるだけでOKです。
DEMO2
※サーバーが非力なので503エラーで画像が表示されない場合があります。すみません・・・
スクロールするとスマホとPCでアスペクト比の違う画像がちゃんと読み込まれますね。
ダミー画像と表示させる画像のアスペクト比が違う場合、画像を置き換えるまでの間、高さがずれてしまいます。
DEMOの場合、ダミーは1pxの透過ですからこうなってしまいます。
遅延読み込みなので仕方ないのですが、ちょっと早くスクロールしたら画面がガクガクしたりして気になりますよね。
他にも例えばページ内リンクを使ったスムーススクロールをJSで実装している場合、スムーススクロールの画面移動の速さに画像の置き換えが追いつかないので、変なタイミングで画像が読み込まれて高さがズレてしまい、止まって欲しい要素に止まらない、といったことが起こります。
かといって、同じサイズのダミー画像なんか用意してられないし、大きいダミー画像なんて用意してたら意味ないですよね。
これを解決する方法がlazysizesの拡張プラグインにあります。
lazysizes aspectratio extension
https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/aspectratio
このプラグインを使うと、指定したアスペクト比でダミー画像の高さを保ってくれます。
拡張プラグインはlazysizesをダウンロードした時にplugins
というディレクトリがありますので、その中にいろいろと入っています。
ここで使うのはls.aspectratio.min.js
なのでファイルをコピーしてきてlazysizesの後に読み込みます。
<script src="assets/js/lazysizes.min.js"></script>
<script src="assets/js/ls.aspectratio.min.js"></script>
CDNももちろんあります。
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/plugins/aspectratio/ls.aspectratio.min.js"></script>
使い方は簡単です。
<picture>
<source
media="(min-width:1440px)"
data-aspectratio="1200/630"
data-srcset="assets/images/lazy-pc@2x.jpg">
<source
media="(min-width:768px)"
data-aspectratio="1200/630"
data-srcset="assets/images/lazy-pc.jpg 1x, assets/images/lazy-pc@2x.jpg 2x">
<img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7"
data-src="assets/images/lazy-sp.jpg"
data-aspectratio="750/900"
alt=""
style="width: 100%;">
</picture>
data-aspectratio
属性に横幅/高さの形で追記します。単位はpxで数値のみでOKです。レスポンシブイメージでアスペクト比が変わる場合は各メディアクエリごとにdata-aspectratio
属性を追記してあげればOKです。
そしてimg
タグの横幅をCSSでwidth: 100%;
に設定します。ここではサンプルなのでstyle属性に直書きしていますが、普通にCSSファイルに書いた方が良いです。
これでダミー画像が小さくでも画像のアスペクト比を保ったままズレずにページ表示できるようになりました。
DEMO3
※サーバーが非力なので503エラーで画像が表示されない場合があります。すみません・・・
表示する時にCSSでアニメーションを付けることもできます。
<picture>
<source
media="(min-width:1440px)"
data-aspectratio="1200/630"
data-srcset="assets/images/lazy-pc@2x.jpg">
<source
media="(min-width:768px)"
data-aspectratio="1200/630"
data-srcset="assets/images/lazy-pc.jpg 1x, assets/images/lazy-pc@2x.jpg 2x">
<!-- class「animation」を追加 -->
<img
class="lazyload animation"
src="data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7"
data-src="assets/images/lazy-sp.jpg"
data-aspectratio="750/900"
alt=""
style="width: 100%;">
</picture>
img
タグにanimation
というclassを追加します。animation
は任意なので何でも構いません。全ての画像に同じアニメーションを設定する場合はなくても大丈夫です。
次にCSSを追記します。
.animation.lazyload,
.animation.lazyloading
{
opacity: 0;
visibility: hidden;
transform: scale(.8);
}
.animation.lazyloaded{
opacity: 1;
visibility: visible;
transform: none;
transition:1s ease;
}
遅延読み込み実行時にlazyloading
、読み込み後にlazyloaded
というclassが付くのでそれを利用してCSSトランジションを書きました。transition
でなくanimation
でもできると思います。
DEMO4
※サーバーが非力なので503エラーで画像が表示されない場合があります。すみません・・・
デフォルトのlazysizesはIntersection Observer APIに対応していません。
ただし、lazysizesをGithubからダウンロードすると、src
ディレクトリにlazysizes-intersection.js
というファイルがあり、こちらはIntersection Observerを使っています。
読み込ませてみたところ、普通に使えるようでしたので、先ほどのDEMO4のJSを入れ替えてみました。
DEMO - Intersection Observer
※サーバーが非力なので503エラーで画像が表示されない場合があります。すみません・・・
Intersection Observerの方がパフォーマンスが良いので不具合がなければこちらを使えた方が良いのになーと思います。ただしpolyfillが必要です。
また、こちらの方法は動きますがおすすめはしません。
Github上にファイルは存在しますがドキュメントには掲載されていませんし、不具合やバグが起きても対応できない可能性がありそうです。 今回のようにDEMOで試すのには問題ないと思いますが、本番で使うことは避けた方が良いでしょう。
Intersection Observer APIについては過去記事がありますのでもし良ければこちらも合わせてご覧ください。
WordPressでは、the_post_thumbnail()
関数を使ってアイキャッチ画像を出力したり、記事内に埋め込んだ画像はthe_content()
で出力される本文に自動的にimg
タグとして入っています。これらをlazysizesに対応させる方法をメモしておきます。
function modify_post_thumbnail_html($html, $post_id, $post_thumbnail_id, $size, $attr) {
if(is_admin() || is_singular('demo')){
return $html;
}
$id = get_post_thumbnail_id();
$src = wp_get_attachment_image_src($id, $size);
$alt = get_the_title($id);
$html = '<img class="lazyload" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mN89wcAAt4B7N+P0IYAAAAASUVORK5CYII=" data-src="'.$src[0].'" alt="'.$alt.'">';
return $html;
}
add_filter('post_thumbnail_html', 'modify_post_thumbnail_html', 99, 5);
post_thumbnail_html
フックを使うとthe_post_thumbnail()
で生成されるhtmlを変更できます。これでclass="lazyload"
やdata-src
など必要な記述を追記できますね。
記事に埋め込んだ画像やiframeに対応する場合です。
こちらはググると対応している方の記事があったので丸々そのまま参考にさせていただきました。
lazySizesでレスポンシブイメージの遅延読み込み(lazy load)をWordPressに組み込む
https://1010uzu.com/blog/lazysizes-responsive-images-lazy-load-wordpress
こちらの記事に書かれているadd_lazyload_tag()
という関数を使わせていただくと、記事内のiframe
タグとimg
タグをlazyload対応できます!
画像の遅延読み込みライブラリはたくさんありますが、正直どれでもいいですし、やろうと思えば自分でスクラッチで実装することも可能です。ただlazysizesはかなり簡単で高機能でなおかつ軽量ということで個人的にすごくおすすめです!まだ使ったことのない方は試してみてください。
また、Google Chromeではデフォルトで遅延読み込みができる機能が付いたみたいです。参考までに。
Chromeがネイティブlazy-loadをサポート、JSなしで画像を遅延読み込み可能に
https://www.suzukikenichi.com/blog/chrome-76-supports-native-native-loading/
ご指摘・ご質問はTwitterにお願いいたします。