Astroで背景画像の最適化にチャレンジしたけど無理だった
2024.05.21 追記
実現できます!コメントありがとうございました!
前回の記事でPictureコンポーネントを自作し、アートディレクションを行うことができました。
今回は背景画像にもアートディレクションと最適化処理についてチャレンジしてみました。
背景画像のアートディレクションは難しい
*アートディレクション:media="(min-width: 1024px)"のような画面幅での切り分け
Astroのフロントマターと呼ばれる「---」で囲われる部分で行なった処理はHTML側でしか呼び出せないため、CSSやJavaScript側で変数を呼び出すことができません。
---
const sampleID = "sample";
const background = "/img/bg.jpg";
---
<script>
// この中では使えない
</script>
<div id={sampleID} style={`background-image:url(${background})`}>HTMLでは使える</div>
<style>
// この中では使えない
</style>
背景画像最適化案1 astro-imagetools
画像コンポーネント以外にも背景画像にも対応し、アートディレクションも行えるパッケージです。
やりたいことが満たされているのでこれでいけそうでしたが、qiitaの記事に書いてある通り不要なコードが入ってきてコントロールが難しそう。
実際に試したけど、breakpointやmedia指定で画面幅指定しても変わってくれなかった。
単純にPC/SP切り替えたいだけの用途にはオーバースペック感。
細かい調整をこちらでコントロールしたい。
背景画像最適化案2 自作する
pictureタグを自作した要領で試したがこれもうまくいかなかった。
フロントマターはHTMLタグにだけ適用できますが、style属性にはmedia指定ができないので以下のような指定では動きません。
---
import { getImage } from 'astro:assets';
const mobileImage = await getImage({ src: '/path/to/mobile-image.jpg', width: 600 });
const desktopImage = await getImage({ src: '/path/to/desktop-image.jpg', width: 1200 });
---
<div
style={{
backgroundImage: `url(${mobileImage.src})`,
'@media (min-width: 960px)': {
backgroundImage: `url(${desktopImage.src})`
}
}}
>これは機能しません。</div>
CSS側にフロントマターの変数を適用することもできませんでした。
---
import { getImage } from 'astro:assets';
const mobileImageUrl = (await getImage({ src: '/path/to/mobile-image.jpg', width: 600 })).src;
const desktopImageUrl = (await getImage({ src: '/path/to/desktop-image.jpg', width: 1200 })).src;
---
<div class="responsive-background"></div>
<style>
// これも機能しません
.responsive-background {
background-image: url('${mobileImageUrl}');
}
@media (min-width: 960px) {
.responsive-background {
background-image: url('${desktopImageUrl}');
}
}
</style>
背景画像最適化案3 Backgroundコンポーネントを自作する
astro-imagetoolsを習ってBackgroundコンポーネントを作ってpropsで渡す形も考えたのですが、結局HTML側で振り分けをする方法がないのでこれもボツになりました。
Astro側で最適化処理を行わないという選択
ちょっと煮詰まったので最初の目的を確認します。
・画像ディレクトリ統一
・画像最適化
・pictureでアートディレクション
・背景画像(CSS)でもアートディレクション
・ビルド時に最適化
「ビルド時に最適化」を外せばAstro使わなくてもできそう。。
ということで無理にAstroでやらなくてもいいんじゃないかという結論に至りました。
Sharpを使って別途最適化処理
全てをpublic
ディレクトリに格納し、別途最適化の処理を行う方法を模索します。
Astro内部でも使っているSharpを個別で使って最適化します。
パッケージをインストール
npm i chokidar sharp
publicのimg配下にあるpngとjpg拡張子のファイルをwebpに変換するスクリプトです。
const sharp = require("sharp");
const chokidar = require("chokidar");
const directoryPath = "public/img/**";
const convertToWebP = (filePath) => {
const outputFilePath = filePath.replace(/\.(png|jpg|jpeg)$/, ".webp");
sharp(filePath)
.toFormat("webp")
.toFile(outputFilePath, (err) => {
if (err) {
console.error("Error converting image:", err);
} else {
console.log(`Converted ${filePath} to WebP`);
}
});
};
const watcher = chokidar.watch(directoryPath, { ignored: /^\./, persistent: true });
watcher
.on("add", (filePath) => {
if (/\.(png|jpg|jpeg)$/.test(filePath)) {
convertToWebP(filePath);
}
})
.on("error", (error) => console.error(`Watcher error: ${error}`));
package.jsonに登録しておき、
"img": "node optimizedImages.js",
npm run img
で処理を走らせます。
ビルド時の最適化という目的は果たせなかったですが、
・画像ディレクトリ統一
・画像最適化
・pictureでアートディレクション
・背景画像(CSS)でもアートディレクション
は達成できました。
画像ディレクトリをpublic内に統一した関係で、前回自作したPictureコンポーネントは不要になってしまったのですが目的は果たせたので良しとします!
背景画像もAstroで最適化する方法あったら教えてください!
Discussion
css 変数を使うと最適化された画像を参照することができます。
で紹介してみました。参考になれば。
うおー!ありがとうございます!
なるほど、
define:vars
使えば確かにいけますね。そもそも知らなかったので参考になりました。
次の案件で取り入れてみますね!