webpのフォールバックには、pictureタグを使うシーンが多いが、pictureタグってネストが深くなるから、HTMLの視認性的に使いたくないシーンもある。
またpictureタグの初心者にとっては、srcsetとは違い、pictureタグを使うことで、スタイルが崩れるかもっていう心配事とかもあったりしてハードルが高くなってしまう傾向もある(実際にはpictureタグはCSSが効かず、直下のimg要素にpicture内で分別されるsrcが入るだけだから影響はない)。
話は逸れるが、srcsetはサイズ(解像度)変更のみに使用するのがベストで、別の画像を使用するなどアートディレクションが必要な場合は、pictureで出し分けることが推奨される。
理由としては、srcsetはそのブラウザ幅で使って欲しい画像を希望しているだけであり、たとえば以下のような指定があった場合に、400px幅のRetinaディスプレイで見ている場合には800.pngが表示されることになる。
<img srcset="./400.png 400w, ./800.png 800w" />
一方、pictureはメディアクエリでの判断なので、以下のような場合には400.pngが強制的に表示されるためである。
<picture>
<source media="(min-width:800px)" srcset="./800.png" />
<source media="(min-width:400px)" srcset="./400.png">
<img src="400.png" alt="" />
</picture>
参考:https://parashuto.com/rriver/responsive-web/picture-srcset-use-case
そこでwebpが使えるブラウザかどうかの判定が必要になる。
判定方法は、サーバーサイドとフロントエンドのどちらでも可能である。
サーバーサイド
ブラウザから送信されるAcceptリクエストヘッダーにimage/webpが含まれていればwebpに対応したブラウザということになる。
Acceptリクエストヘッダーはリクエストを行う対象に応じて値が変わるため、HTML取得時ではなく、画像を対象としてAcceptリクエストヘッダーを確認する必要がある。
一般的には、.htaccessでアクセスを振り分けるために使用することが多い。
たとえば、以下のように使用する。
引用元:https://www.ones-quest.co.jp/labo/webp02.php
<IfModule mod_rewrite.c>
# 利用環境で mod_rewrite(モジュール)が利用できるかの確認。
RewriteEngine On
# mod_rewrite(モジュール)の機能を有効化する。無効化する場合は Off。
RewriteCond %{HTTP_ACCEPT} image/webp
# ブラウザから送信される Accept リクエストヘッダに image/webp が含まれるか(WebP をサポートしているかどうか)を確認。
RewriteCond %{REQUEST_URI} (?i)(.*)(\.jpe?g|\.png)$
# 「リクエストされた URI」に .jpg, .jpeg, .JPG, .JPEG, .png, .PNG の拡張子が含まれているかを判定。 正規表現のメタ文字 (?i) で大文字・小文字を同一視。
RewriteCond %{DOCUMENT_ROOT}%1%2.webp -f
# WebP 置換画像が存在するか確認。
RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1$2\.webp [L,T=image/webp,R]
# 上記の指定する条件を満たせば、JPEG や PNG 画像の代わりに WebP 画像を提供。
</IfModule>
<IfModule mod_headers.c>
# 利用環境で mod_headers(モジュール)が利用できるかの確認。
Header append Vary Accept env=REDIRECT_accept
# mod_headers が有効な場合、Vary レスポンスヘッダに Accept を追加して、ブラウザの機能に応じて WebP と JPEG / PNG をそれぞれキャッシュできるようにキャッシュサーバーに示す。
</IfModule>
<IfModule mod_mime.c>
# 利用環境で mod_mime(モジュール)が利用できるかの確認。
AddType image/webp .webp
# 拡張子.webp ファイルは Content-Type として image/webp を返す設定。
</IfModule>
# RewriteCond について
# RewriteCond は RewriteRule を実行するための条件を定義するための記述で、RewriteRule 記述の直前に複数記述することができ、 RewriteCond が指定する条件を満たすと RewriteRule が実行される。
# RewriteCond の書式
# RewriteCond %{変数名}(テスト文字列) 条件パターン(正規表現を記述) [フラグ]
# RewriteRule URL書換&転送設定を記述
# テスト文字列に利用できる環境変数
# HTTP_ACCEPT
# ブラウザがサポートするメディア。MIMEタイプ「ファイルの種類を表す情報」を用いてデータの形式を指定。
# REQUEST_URI
# リクエストされた URI 情報。特定の種類のファイルのみリダイレクト。
# DOCUMENT_ROOT
# サーバーのドキュメントルートのパス。
# 用意した画像 xxx.png とそれに対応する WebP 画像 xxx.png.webp の画像がある場合
# %2 、$2 を記述する。
# RewriteCond の条件パターンで「()(カッコ)」を使って一致した値は、変数を使って再利用できる。これを後方参照と言い、 RewriteCond で指定したパターンを参照する場合は「%(パーセント)」を使う。「()(カッコ)」が複数ある場合は左から %1、%2 と利用することができる。
# 同じように、 RewriteRule でも後方参照を利用でき、こちらは一般の正規表現の後方参照と同じように「$(ドル)」で利用できる。
フロントエンド
フロントエンドではUAで判断する方法と、実際にwebp画像を読み込ませて画像として認識できるかどうかを判断する方法の2種類がある。
UAは一般的にバッドプラクティスであるから、機能で判断できる後者の方法がよい方法である。
具体的には以下のように実装する。Modernizr.jsでも同じような方法で実装しているようだ。
参考:https://azujuuuuuun.hatenablog.com/entry/2021/08/18/223952
// 1px四方のWebP画像をJavaScript経由で書き込んでみて、画像として認識されるかを確認
// もし画像として認識されれば画像サイズが取得できる
const img = new Image();
img.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
console.log(img.witdh); // 1