Astroのintegrationsの一つである@astrojs/imageを追加することでAstroに画像の最適化機能を追加することができます。本文書では@astrojs/image integrationが提供するImport, Pictureコンポーネントを利用して、最終的に出力されるimg, pictureタグがどのような設定になっているかを確認していきます。

@astrojs/imageの設定だけではなく@astro/imageはシンプルな機能なのでpictureタグの使い方を知りたい?という人や画像の最適化ではどのようなことを行うの?と疑問に思う人にも参考になる内容になっています。

Astroの公式のツールとして画像の最適化にはAstro2.1から機能追加され現在開発中のAssetsがあります。Assetsは@astrojs/imageの後継機能なのでAPIは異なってもコアな部分は同じだと思うので@astrojs/imageの理解はAssetsにも活かすことができます。

Astroについてはこちらの記事で公開済みです。

プロジェクトの作成

Astroの@astrojs/image integrationの動作確認を行う環境を構築するためnpm create astroコマンドでプロジェクトの作成を行います。コマンド実行後ににいくつか質問があります。質問の一つであるテンプレートでは”samle files”を選択しています。残りの質問はすべてデフォルトを選択しています。プロジェクト名には任意の名前をつけることができますがここではastrojs-imageという名前を設定しています。


 % npm create astro@latest

╭─────╮  Houston:
│ ◠ ◡ ◠  Time to build a sweet new website.
╰─────╯

 astro   v2.5.3 Launch sequence initiated.

   dir   Where should we create your new project?
         ./astrojs-image

  tmpl   How would you like to start your new project?
         Include sample files
      ✔  Template copied

  deps   Install dependencies?
         Yes
      ✔  Dependencies installed

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict
      ✔  TypeScript customized

   git   Initialize a new git repository?
         Yes
      ✔  Git initialized

  next   Liftoff confirmed. Explore your project!

         Enter your project directory using cd ./astrojs-image 
         Run npm run dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯
mac@macnoMBP Desktop % cd astrojs-image 

コマンドが完了するとastrojs-imageフォルダが作成されるのでフォルダに移動してpackage.jsonの中身を確認しておきます。


{
  "name": "astrojs-image",
  "type": "module",
  "version": "0.0.1",
  "scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "astro": "astro"
  },
  "dependencies": {
    "astro": "^2.4.1"
  }
}

コンポーネントの作成

画像最適化の動作確認を行うためのコンポーネントの作成を行います。componentsフォルダにAstroImage.astroファイルを作成します。


<h1>@astrojs/imageの動作確認</h1>

作成したAstroImageコンポーネントをimportするためにpagesフォルダのindex.astroを以下のように書き換えます。


---
import Layout from '../layouts/Layout.astro';
import AstroImage from '../components/AstroImage.astro';
---

<Layout title="Welcome to Astro.">
  <main>
    <AstroImage />
  </main>
</Layout>

npm run devコマンドを実行して開発サーバを起動してブラウザからhttp://localhost:3000にアクセスします。ブラウザ上にはAstroImageに記述した内容が表示されます。

初期ページの確認
初期ページの確認

画像の表示

Astroでは画像を表示する方法がいくつかあるので@astrojs/imageの動作確認を行う前に@astrojs/imageを利用しない場合の画像の表示方法について確認しておきます。

publicフォルダ

デフォルトから存在するpublicディレクトリにbird.jpgファイルを保存します。imgタグのsrcにファイルのパスを設定しますがpublicフォルダ内のファイルのパスを設定する場合には/public/bird.jpgではなくpublicを省略して/bird.jpgとします。


<h1>@astrojs/imageの動作確認</h1>
<img src="/bird.jpg" width="500px" />

ブラウザ上には鳥の画像が表示されます。

画像の表示
画像の表示

publicフォルダにある画像をimportすることで表示することもできます。


---
import bird from '/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<img src={bird} width="500px" />

どちらの場合もブラウザのデベロッパーツールでsrcの値を確認すると/bird.jpgになっていることが確認できます。


<img src="/bird.jpg">

src下のフォルダ

srcフォルダに任意のフォルダを作成してその下に画像を保存します。ここではimagesフォルダを作成してbird.jpgファイルを保存します。

index.astroファイルはpagesフォルダに保存されているのでpagesフォルダからの相対パスで設定を行います。


---
import bird from '../images/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<img src={bird} width="500px" />

publicと同様の画面が表示されます。

publicフォルダに保存したファイルも相対パスで”../../public/bird.jpg”で設定することは可能です。

開発環境の場合はデベロッパーツールで要素を確認すると以下のようなパスとなり相対パスではなくなり/src/がつきます。


<img src="/src/images/bird.jpg">

ビルドを実行するとどのような変化があるか確認します。ビルドはnpm run buildで実行できます。ビルドを実行すると画像はdist/_astroディレクトリに保存されデベロッパーツールの要素を確認すると以下のようなパスとなります。


<img src="/_astro/bird.6fede37f.jpg">

publicフォルダに保存して<img src=”/bird.jpg” />で設定した場合はビルド後はdistフォルダの直下にそのままの名前bird.jpgで保存されimgタグのパスも開発時とビルド時で同じです。

@astro/images

@astro/images integrationのインストールをすることでAstroに画像最適化機能を追加することができます。@astro/imagesのインストールを行いAstroへのintegrationの登録が完了するとImage, Pictureの2つのコンポーネントが利用できるようになります。

@astro/imagesのインストール

@astro/images Integrationのインストールを行います。


 % npx astro add image
✔ Resolving packages...

  Astro will run the following command:
  If you skip this step, you can always run it yourself later

 ╭──────────────────────────────────────────╮
 │ npm install @astrojs/image astro@^2.5.0  │
 ╰──────────────────────────────────────────╯

✔ Continue? … yes
✔ Installing dependencies...

  Astro will make the following changes to your config file:

 ╭ astro.config.mjs ─────────────────────────────╮
 │ import { defineConfig } from 'astro/config';  │
 │                                               │
 │ import image from "@astrojs/image";           │
 │                                               │
 │ // https://astro.build/config                 │
 │ export default defineConfig({                 │
 │   integrations: [image()]                     │
 │ });                                           │
 ╰───────────────────────────────────────────────╯

✔ Continue? … yes
  
   success  Added the following integration to your project:
  - @astrojs/image

インストール中にastro.config.mjsファイルへ@astrojs/imageを行うかどうか聞かれます。許可するとintegrationとしてインストールした@astrojs/imageが登録されAstroで利用できるようになります。インストールが完了してもこの設定を行っていない場合は画像最適化の機能は利用できません。開発時に一時的に機能を利用したくない場合は追加された行をコメントとしてください。


import { defineConfig } from 'astro/config';

import image from "@astrojs/image";

// https://astro.build/config
export default defineConfig({
  integrations: [image()]
});

その他の初期設定としてドキュメントに記載されている通りenv.d.tsファイルも更新します。デフォルトでは下記のように設定されています。


/// <reference types="astro/client" />

下記のようの更新を行います。


/// <reference types="@astrojs/image/client" />

インストール後に発生するimportエラー

インストール前にはimageフォルダのbird.jpgファイルをimport文を利用して表示することができましたがインストールが完了するとエラーにより画像が表示されません。


---
import bird from '../images/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<img src={bird} width="500px" />

原因は@astrojs/image機能の追加によりimportを実行後のbirdに含まれる値が文字列からオブジェクトになったためです。

console.logを利用してbirdの中身を確認するとsrc, width, height, formatを含んだオブジェクトになっていることが確認できます。imgタグのsrc属性は文字列のみ受け入れるためオブジェクトではエラーになります。


{
  src: '/@astroimage/Users/mac/Desktop/astrojs-image/src/images/bird.jpg',
  width: 1280,
  height: 852,
  format: 'jpg'
}
VS Codeを利用している場合はsrc属性に対して”Type ‘ImageMetadata’ is not assignable to type ‘string’.”のメッセージが表示されます。

Imageコンポーネント

@astrojs/imageの機能追加が完了するとImageコンポーネントが利用できるようになります。

Imageコンポーネントの設定

import文を使って画像の設定を行ったsrcフォルダのimagesフォルダに保存したファイルbird.jpgを使って設定を行います。Imageコンポーネントはimportする必要があります。Imageコンポーネントではsrc, alt propsを設定する必要があります。


---
import { Image } from '@astrojs/image/components';
import bird from '../images/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<Image src={bird} alt="a bird" />

Imageコンポーネントを利用後に表示される画像は下記のとおりです。

Imageコンポーネントを利用した画像表示
Imageコンポーネントを利用した画像表示

表示されている画像の要素をデベロッパーツールで確認すると以下のような設定が行われていることがわかります。


<img alt="a bird" width="1280" height="852" src="/@astroimage/Users/mac/Desktop/astrojs-image/src/images/bird.jpg?f=jpg&w=1280&h=852&href=%2F%40astroimage%2FUsers%2Fmac%2FDesktop%2Fastrojs-image%2Fsrc%2Fimages%2Fbird.jpg" loading="lazy" decoding="async">

width, heightを設定することで画像のダウンロードに時間がかかる場合にブラウザ上に画像の表示領域を確保してくれるためLayout Shiftを防ぐことができます。画像の下にコンテンツがありwidth, heightが設定されていない場合に画像のダウンロードに時間がかかると画像が表示される場所にコンテンツが表示され、画像が表示されるとコンテンツがその下に移動するLayout Shiftが発生します。

loadingをlazyに設定することで画像の遅延読み込みを行います。アクセス時にブラウザのビューポート外に配置されている画像はユーザがスクロールしてビューポートに近づいたタイミングで画像の読み込みが行われます。decodingのasyncは画像を表示するためのデコード処理を画像以外のコンテンツ処理を妨げずに非同期で行うための設定です。loadingとdecoding属性を設定することでページの表示速度を最適化しています。

要素の他にデベロッパーツールのネットワークタブでサイズを確認します。Size列を確認すると74.0kBであることがわかります。実際の画像のファイルサイズはOS上では128KBなので画像の圧縮も行われていることがわかります。

ネットワークタブで画像のサイズを確認
ネットワークタブで画像のサイズを確認

ここまでの動作確認でImageコンポーネントを利用するとimgタグにloading, decode属性が追加され画像が圧縮されるということが確認できました。

パラメータ

Imageコンポーネントを利用するだけでデフォルトで様々な設定が行われることが確認できましたがそれらの設定をパラメータを利用して制御することができます。

quality

qualityは名前の通り画質に関する設定です。画像を最適化する際の圧縮に影響を与えます。qualityの値を小さくすると画像のサイズは小さくなりますが画像が荒くなります。試しに10を設定してみましょう。


<Image src={bird} alt="bird" quality={10} />

先ほどと比べて明らかに画像が荒くなっていることがわかります。ネットワークタブで確認するとサイズは11.2KBになっているので画像の質は落ちていますが落ちた文だけサイズがかなり圧縮されていることもわかります。

qualityの値を変更
qualityの値を変更

format

formatでファイルのフォーマットを指定することで変換するファイルのフォーマットを変更することができます。formatを指定しない場合は元のファイルのフォーマットで作成されます。今回はjpgファイルなのでデフォルトではjpgファイルが作成されます。

動作確認のためformatをwebpに変更します。


<Image src={bird} alt="bird" format="webp" />

formatをwebpに設定後、ネットワークタブで確認するとサイズが63.1KBになっていることが確認、Typeを確認すると設定した”webp”になっていることがわかります。

SizeとType列の確認
SizeとType列の確認

画像をドラッグ&ドロップでデスクトップにダウンロードするとbird.webpと拡張子もwebpになっていることが確認できます。formatではwebpの他にavifなども指定することができます。

デフォルトではquantityの値を10にすると画像の質が非常に悪くなりましたがwebpに設定するとそれほど画像の質が悪くなりません。webpやavifによって質を落とさずにサイズを小さくするといったことが可能になります。quantityやformatの値を変更してアプリケーションにとって適切な値を見つけてください。

width, height, aspectRatio

width, heightを明示的に指定すると指定したサイズで画像が表示されます。widthのみを設定するとheightは自動で設定されます。heightのみを設定するとwidthは自動で設定されます。widthとaspectRatioを組み合わせて設定することもできます。下記ではaspectRatioを1/1に設定しているでwidthを500とするとheightは500となります。


<Image src={bird} alt="a bird" width={500} aspectRatio={1 / 1} fit="cover" />

ブラウザで確認すると以下のように横幅が500になっていますが横幅が縮んでいることがわかります。

500X500で表示
500X500で表示

sharpのインストール

@astrojs/imageでは画像を変換する際にデフォルトでSqooshが利用されています。Sqooshは画像を圧縮する際に利用できるツールです。

Imageコンポーネントのパラメータには他にもfitやpositionなどがありますがそれらの機能はデフォルトSqooshでは動作しません。fitやpositionのパラメータを利用するためには画像処理ライブラリであるsharpをインストールする必要があります。


 % npm install sharp

sharpをインストール後はastro.config.mjsファイルを更新してsharpを画像処理に利用できるように設定を行います。


import { defineConfig } from 'astro/config';
import image from '@astrojs/image';

// https://astro.build/config
export default defineConfig({
  integrations: [
    image({
      serviceEntryPoint: '@astrojs/image/sharp',
    }),
  ],
});

sharpではfitを利用して画像のリサイズを行うことができます。先ほど画像が横に縮んだ時と同じwidthとapectRatioの設定で画像を確認します。


<Image src={bird} alt="a bird" width={500} aspectRatio={1 / 1} />

sharpのリサイズにより縮むのではなく左右の画像が切り取られていることがわかります。

sharpインストール後の画像
sharpインストール後の画像

fitは5つの値を利用することができるので各値を設定することでどのような違いがあるか確認します。

デフォルトではcoverが設定されていますがwidthとheightの値を300に設定して動作確認を行います。


---
import { Image } from '@astrojs/image/components';
import bird from '../images/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<Image src={bird} alt="bird" width={300} height={300} fit="cover" />
<Image src={bird} alt="bird" width={300} height={300} fit="contain" />
<Image src={bird} alt="bird" width={300} height={300} fit="fill" />
<Image src={bird} alt="bird" width={300} height={300} fit="inside" />
<Image src={bird} alt="bird" width={300} height={300} fit="outside" />
fitの値による表示画像の違い
fitの値による表示画像の違い

左側から準備にcover, contain, fill, inside, outsideとなります。fill, inside, outsideは同じ画像表示となっていますがデベロッパツールを利用して画像のサイズを確認するとinsideの場合はリサイズ後のサイズが300px X 200px、outsideの場合は451px X 300pxとなっています。ネットワークタブのpreviewを見ても変換された画像が正方形の画像ではない確認できます。imgタグに設定されたwidth, heightで300 X 300で表示されています。

previewで画像のサイズを確認
previewで画像のサイズを確認

background

fitの値がcontainについては背景が黒になっていますがbackgroundパラメータを利用することで背景色を変更することができます。

白に設定したい場合には以下のように設定を行うことができます。


<Image
  src={bird}
  alt="bird"
  width={300}
  height={300}
  fit="contain"
  background="rgb(255,255,255)"
/>

中央の画像がfitをcontainに設定してbackgroundで背景を白にした場合です。

backgroundを設定した場合
backgroundを設定した場合

position

positionの値の動作確認を行うためにfitをcoverに設定し、positionの値をデフォルトとright, leftの値に設定して違いを確認します。


<Image src={bird} alt="bird" width={300} height={300} fit="cover" />
<Image
  src={bird}
  alt="bird"
  width={300}
  height={300}
  fit="cover"
  position="left"
/>
<Image
  src={bird}
  alt="bird"
  width={300}
  height={300}
  fit="cover"
  position="right"
/>

左からデフォルト、left, rightとなります。leftは左側から、rightは右から表示されていることがわかります。

positionの動作確認
positionの動作確認

Imageコンポーネントのパラメータを利用することで画像をカスタマイズできることがわかりました。

Pictureコンポーネント

Imageコンポーネントを利用するとimgタグとして出力されますが、Pictureコンポーネントを利用した場合は1つのタグではなくpictureタグ、sourceタグ、imgタグの複数のタグが組み合わさせた状態で出力されデバイスやユーザの環境に合わせて表示する画像を変更することができます。

Pictureコンポーネントの設定

Pictureコンポーネントにも複数のパラメータが準備されておりsrc, widths, sizes, altが必須となっています。

動作確認のため下記の設定を行います。


---
import { Picture } from '@astrojs/image/components';
import bird from '../images/bird.jpg';
---

<h1>@astrojs/imageの動作確認</h1>
<Picture
  src={bird}
  widths={[640, 768, 1024]}
  sizes="(max-width: 1200px) 100vw,1200px"
  alt="bird"
/>

設定後、ブラウザのデベロッパーツールで要素を確認すると以下のタグが設定されてることがわかります。

Pictureコンポーネントで表示される内容
Pictureコンポーネントで表示される内容

sourceタグのsrcやimgタグのsrcタグに設定されているパスが長いので省略すると下記のようになっています。


<picture>
  <source type="image/avif" srcset="/bird_640.jpg 640w,/bird_768.jpg 768w,/bird_102.jpg 1024w" sizes="(max-width: 1200px) 100vw,1200px">
  <source type="image/webp" srcset="/bird_640.jpg 640w,/bird_768.jpg 768w,/bird_1024.jpg 1024w" sizes="(max-width: 1200px) 100vw,1200px">
  <source type="image/jpeg" srcset="/bird_640.jpg 640w,/bird_768.jpg 768w,/bird_1024.jpg 1024w" sizes="(max-width: 1200px) 100vw,1200px">
  <img alt="bird" src="/bird.jpg" loading="lazy" decoding="async">
</picture>

デフォルトの設定でavif, webp, jpegのフォーマットでそれぞれ3つのファイルが作成されています。3つのファイルが作成されるのはPicureコンポーネントのsizes propsの設定で640, 768, 1024を指定しているためです。

この設定により下記のような流れで画像が表示されます。

デバイスの画面の横幅が640pxまででavifをサポートしているブラウザの場合は一番上のtype=”image/avif”のsourceタグに設定されたbird_640.jpgの画像が表示されます。avif、webpに対応しておらずデバイスの画面の横幅が800pxの場合は3番目のtypeが”image/jpg”のsourceタグで設定されたbird_1024.jpgが表示されます。

sourceの条件に合わないデバイスでアクセスがあった場合には最終的にimgタグで設定した画像が表示されます。

type属性によってブラウザがサポートしているファイルタイプをチェックし、srcset属性の設定によってアクセスしてきたデバイスの横幅のチェックを行い、表示するファイルを振り分けています。

sizes属性の設定”(max-width: 1200px) 100vw,1200px”ではブラウザの横幅が1200pxまでは画面一杯に広がりますが1200pxを超えるとサイズ1200pxの画像として表示されます。

npm run buildによりビルドを行うとdist/.astroフォルダにはjpgファイルが3つ、webpファイルが3つ、avifファイルが3つ+元の画像の計10枚のファイルが作成されます。

パラメータ

Pictureコンポーネントでは以下のパラメータを利用することができます。

src, alt, sizes, widths, aspectRatio, fit, background, position, formats, loading, decoding。

設定できるパラメータについてはすべてImageコンポーネントと先ほど確認したPictureコンポーネントで動作確認が完了しています。

まとめ

@astrojs/image integrationは後継としてAssetsが開発中なので将来的な利用頻度は高くはありません。しかしほとんどの機能はAssetsに引き継がれていくこと、pictureタグ、sourceタグの利用方法、sharpライブラリによる画像の変換とリサイズについてはAstroに限らない一般的なものなので画像の最適化を行う際に参考にできるかと思います。