レスポンシブ対応時にも画像の表示場所をあらかじめ確保することで、画像の遅延読み込み時 (Lazy loading) に再レンダリングされるのを防止して画面がチラつくのを回避する方法をまとめました。
目次
レスポンシブ画像の遅延読み込みで高さが指定されていない場合再レンダリングされてしまう原因
去年画像の lazySizes というライブラリを使って遅延読み込み (Lazy loading) に対応しました。
レスポンシブ対応の画像を遅延読み込みすると、スクロールなどで後から読み込んだ画像を表示するとき、ページ全体が再レンダリングされてチラついてみっともないです。また記事に目次を作っているとリンクをクリックしたとき、ページ内を移動するのだけれど、まだ読み込んでない画像がある場合、ジャンプ後の位置が画像を読み込むと変わるため、意図した所にジャンプしてくれないなどの問題点がありました。イメージとしては下記のアニメーションのような感じです。
なぜそうなるかというと画像をレスポンシブデザインに対応させるには以下のような CSS を指定して、画面サイズに合わせて画像の大きさを変化させます。
img {
width: 400px;
max-width: 100%;
height: auto;
}
このとき height
が auto
になっているため、画像が読み込まれていない状態ではブラウザは高さがわからないため、高さを 0
として一度レンダリングします。そして画像が遅延読み込みされたときに画像の高さが初めてわかるので、それを元に再度レンダリングを行うので画面がチラつくのです。
そこで以下のサイトを参考にして記事内にどんな大きさの画像が配置されても、レスポンシブデザインにおいても画像が表示される場所を確保することにしました。
→ Davide Calignano : Lazy loading with responsive images and unknown height
施策後のイメージは以下のアニメーションのような感じです。
画像遅延読み込み時に画像の表示スペースを確保する基本的な考え方
基本的な考え方は次のとおりです。
img
タグのひとつ外側の a
要素に height
を 0
にして padding-bottom
を画像の縦横比(アスペクト比)で設定して余白を確保します。
padding-bottom = ( 高さ ÷ 横幅 ) × 100 (%);
padding-bottom
は %
で指定した場合、親要素の横幅を基準にするので、 a
タグをさらに div
で囲みこれに幅を指定します。囲った div
タグの幅がすべて同じなら CSS ファイルで width
を指定すればいいですが、横幅が画像によって変わる可能性があるので style
要素でタグ内に埋め込むことにしました。 width
で指定した値よりこの div
要素の表示領域の横幅が狭い場合に備えて max-width: 100%;
もつけ加えます。
そして a
タグの左上を基準にして画像を絶対位置にて表示させます。このとき width
と height
は 100%
と指定して、親要素である a
タグいっぱいに広がるようにします。
これなら画面サイズによって画像の大きさを変化させても、画像のスペースがきちんと確保されており、かつ画像は絶対位置で表示されるので再レンダリングされません。
HTML コード
HTML コードはこんな感じになります。(見やすいように改行を入れました。)
<div class="imageblock" style="width:400px;">
<a href="http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359.jpg" data-size="4000x3000" class="lazy-container" style="padding-bottom:75%;">
<img data-src="http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359-400x300.jpg" alt="雲がかかった富士山"
width="400" height="300"
class="aligncenter size-medium wp-image-252 lazyload"
data-srcset="http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359-400x300.jpg 400w, http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359-320x240.jpg 320w, http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359-768x576.jpg 768w, http://theme.1010uzu.com/wp/wp-content/uploads/2017/03/P1040359-800x600.jpg 800w"
data-sizes="(max-width: 400px) 100vw, 400px" />
</a>
</div>
CSS コード
CSS はこんな感じです。
.imageblock{
max-width: 100%;
}
.lazy-container {
display: block;
position: relative;
height: 0;
width: 100%;
}
.lazy-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
WordPress で対応する場合の関数
WordPress でメディアライブラリから画像を貼りつけるときにコードを自動的に改変する関数を作りました。
WordPress では wp_make_content_images_responsive()
という関数を the_content
にフィルターフックすることで、 srcset
属性や sizes
属性をつけ加えてレスポンシブ対応の img タグに書き換えています。
それにならって必要な style 属性を追加する関数を作成しました。コンテンツの中から <a><img></a>
という部分の HTML タグの配列を作って、それぞれの width
と height
の値を正規表現で検索し、コールバック関数でタグ書き換えています。
image_send_to_editor
のフックを使って、編集画面に挿入するときに書き換えようかと思ったのですが、前の記事にも対応できるように the_content
を使いました。
function my_add_padding_image_wrap( $content ) {
if ( ! preg_match_all( '/<a [^>]+><img [^>]+><\/a>/', $content, $matches_img ) ) {
return $content;
}
$selected_images = array();
foreach( $matches_img[0] as $image ) {
if ( strpos( $image, ' width=' ) !== false && strpos( $image, ' height=' ) !== false ) {
$selected_images[$image] = preg_replace_callback(
'/(<a [^>]*href=".+?")(><img src=".+?" [^>]* width=")([0-9]*?)(" height=")([0-9]*?)(".*?><\/a>)/',
function( $matches ){
return '<div class="imageblock" style="width:' . $matches[3] . 'px;">' . $matches[1] . ' class="lazy-container" style="padding-bottom:'. round( ( $matches[5] / $matches[3] * 100 ), 2 ) . '%;"' . $matches[2] . $matches[3] . $matches[4] . $matches[5] . $matches[6] . '</div>';
},
$image
);
}
}
foreach ( $selected_images as $image => $image_replaced ) {
$content = str_replace( $image, $image_replaced, $content );
}
return $content;
}
add_filter( 'the_content', 'my_add_padding_image_wrap', 9 );
このコードを function.php に書き足すと上に書いた HTML コードが出力されます。クラスに lazyload
を追加する関数に組み込んだ方が効率的かもしれません。わたしは画像を Google Photos に置いているのでこのコードは使ってはいませんが参考までに。