webp は画像サイズも小さく、ウェブで積極的に使っていきたい拡張子です。ですが、ブラウザによっては対応してたりしてなかったりします。そのため、対応状況によって background-image で webp とそうでない画像を出力し分ける一例を紹介します。
※ 今回はあくまで css background-image の対応です。img タグを出し分けたい場合は、HTML の<picture>
と<source type="image/webp">
タグを使うだけで出し分け可能なので、今回付与されるクラスによって出し分けるということはやめましょう。
Javascript で webp に対応しているかどうか判定する
まず最初に、webp 対応しているかどうかは modernizr を使う手があります。自分の場合は webpack 5 で上手く行かなかったので、今回は使っていません。
それでは Javascript でユーザーのブラウザが webp に対応しているか判定します。コードは下記サイトのコピペのため、詳細はサイトをご確認ください。
- https://davidwalsh.name/detect-webp
- https://ourcodeworld.com/articles/read/630/how-to-detect-if-the-webp-image-format-is-supported-in-the-browser-with-javascript
webp 判定関数
/**
* ブラウザが webp をサポートしているかどうか
*
* @returns webp をサポートしているなら true そうでないなら false
*/
export const supportsWebp = async () => {
if (!self.createImageBitmap) return false;
// webp の仮データ
const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
const blob = await fetch(webpData).then((r) => r.blob());
return createImageBitmap(blob).then(
() => true,
() => false,
);
};
- createImageBitmap … webp 判定で使うため、対応していなければ false。IE はここで弾かれる。
webpData
… base64 エンコードされた仮の webp 画像
createImageBitmap
で blob
データからビットマップを生成しますが、この返り値で webp に対応しているかどうか判定されています。
参考にした記事が少し古いので、もしかしたらもっといいやり方があるのかもしれません。ただ、今後 IE が消え、safari も対応となるとこの判定文は不要になるのかもしれません。
webp 用のクラス付与関数
先程作った判定用関数supportWebp()
で、body
に対してクラスを付与します。後述する sass でこのクラスを使います。
/**
* webp 対応していれば target に is-webp、対応していなければ is-no-webp クラスを追加する
*
* @param {string} target (default: body)
*/
const addWebpDetectionClass = (target = 'body') => {
if (supportsWebp()) {
$(target).addClass('is-webp');
} else {
$(target).addClass('is-no-webp');
}
};
addWebpDetectionClass(); // 実行
- webp に対応している … .is-webp
- webp に対応していない … .is-no-webp
がそれぞれ body に付与されるはずです。クラス名はお好きなものを設定してみてください。
Sass で background-image を出し分ける mixin
先程付与したクラスに対して、背景画像を出し分けるための mixin を定義します。この mixin を使うと、簡単に背景画像を出し分けることができます。
scss ファイル
// ## webp classes
$webpClassName: '.is-webp';
$noWebpClassName: '.is-no-webp';
// $selector にする background-image で、webp を出し分ける
// ※親要素に $webpClassName / $noWebpClassName が付与されることが前提
// --------------------------------------------------------
// $selector {string} css セレクタ (default: jpg)
// $filePath {string} 背景画像ファイルパス
// $fileExtension {string} 背景画像拡張子
@mixin webpBg($selector, $filePath, $fileExtension: 'jpg') {
#{$noWebpClassName} #{$selector} {
background-image: url(#{$filePath}.#{$fileExtension});
}
#{$webpClassName} #{$selector} {
background-image: url(#{$filePath}.webp);
}
}
使い方
# jpg 画像
@include webpBg('.my-background', './img/my-bg');
# jpg 以外の画像
@include webpBg('.my-background', './img/my-bg', 'png');
.my-background
セレクターに対して、./img/my-bg.jpg
か ./img/my-bg.webp
の画像が自動的に出し分けされます。jpg
は第3引数を省略可能です。
Sass が使えない時
CSS TRICKS のこちらのセクションが参考になります。やってることは mixin 部分を全て手書きしているだけです。