Details Tech Note

A blog explaining how to improve efficiency and performance of image implementation in development environment templates. The background is a dark blue and the text is white. The blog is set in a modern design with white text on a light blue background. There are circles on the blue background that represent links to other pages on the website. The main font is Arial, and the secondary font is Trebuchet MS. The color of the text is navy blue.

画像を表示するのに、Pug ミックスイン +img を使います。

+img({ src: '/assets/img/ページ名/pc/1x/example_1.png', alt: 'altテキスト' })

実装手順

画像の書き出し

弊社では、デザイナーが Photoshop で書き出しが必要なすべてのレイヤーの名前に example.png のようなファイル名を付けるルールになっています。
そうすることで、画像アセットの機能を使ってすべての画像を自動で書き出せるようになります。なので、エンジニアがいちいちファイル名を決めてリネームする必要はありません。
画像アセットのデフォルト設定として、PC 用 PSD では default 200% 2x/@2x, 1x/、スマートフォン(以下 SP)用 PSD では default 100% 2x/@2x, 50% 1x/(PC 用 PSD のデザインは等倍だが、SP 用は 2 倍サイズで作成しているため)としているので、書き出された画像は以下のようになります。

PC
├── 1x
│   ├── example_1.png
│   └── example_2.png
└── 2x
    ├── example_1@2x.png
    └── example_2@2x.png
SP
├── 1x
│   ├── example_1.png
│   └── example_2.png
└── 2x
    ├── example_1@2x.png
    └── example_2@2x.png

srcディレクトリに画像を配置

以下のように、src/www/assets/imgディレクトリ内に書き出した画像を配置します。
ページ名(名前は任意)ディレクトリやpc,spディレクトリは手動で作成しますが、それ以下は画像アセットで生成されたディレクトリ構造のまま配置するだけです。

src/www/assets/img
└── ページ名
    ├── pc
    │   ├── 1x
    │   │   ├── example_1.png
    │   │   └── example_2.png
    │   └── 2x
    │       ├── example_1@2x.png
    │       └── example_2@2x.png
    └── sp
        ├── 1x
        │   ├── example_1.png
        │   └── example_2.png
        └── 2x
            ├── example_1@2x.png
            └── example_2@2x.png

ちなみに、example_1.png の画像に関連するファイルは以下の 4 つとなります。

├── pc
│   ├── 1x
│   │   └── example_1.png
│   └── 2x
│       └── example_1@2x.png
└── sp
    ├── 1x
    │   └── example_1.png
    └── 2x
        └── example_1@2x.png

Pug で記述

Pug
+img({ src: '/assets/img/ページ名/pc/1x/example_1.png', alt: 'altテキスト' }).example

+imgミックスインを使い、引数オブジェクト(オプション)のsrcプロパティにPC 用の等倍画像のパスだけ指定すれば、それに関連する画像をすべて自動で取得し、以下のように<picture>タグに展開してあらゆる環境に対応した状態で出力されます。

コンパイル後のHTML
<picture>
  <source srcset="/assets/img/ページ名/sp/1x/example_1.png.webp 1x,/assets/img/ページ名/sp/2x/example_1@2x.png.webp 2x" type="image/webp" media="(max-width: 767px)">
  <source srcset="/assets/img/ページ名/pc/1x/example_1.png.webp 1x,/assets/img/ページ名/pc/2x/example_1@2x.png.webp 2x" type="image/webp">
  <source srcset="/assets/img/ページ名/sp/1x/example_1.png 1x,/assets/img/ページ名/sp/2x/example_1@2x.png 2x" media="(max-width: 767px)">
  <img srcset="/assets/img/ページ名/pc/1x/example_1.png 1x,/assets/img/ページ名/pc/2x/example_1@2x.png 2x" src="/assets/img/ページ名/pc/1x/example_1.png" alt="altテキスト" width="923" height="511" loading="lazy" class="example">
</picture>

以下のコードはすべて自動で出力されます。

  • 画像と同じサイズ指定するwidth,height属性
  • 等倍/2 倍の画像を切り替えるsrcset属性
  • PC/SP の画像を切り替えるmedia属性
  • WebP/通常画像を切り替えるtype="image/webp"属性
  • 遅延読み込みさせるloading="lazy"属性

以降の章でそれぞれ詳しく説明します。

width,height属性

<img>タグにwidth, height属性を付けるとレイアウトシフト[1]を防ぐことができますが、全部の<img>タグに手動で付けるのは大変なので、自動化をしています。

まず、gulp-dataを使い、Node.js で定義したimageSize関数を Pug ファイル内から使用できるようにします。
その関数内でimage-sizeを使い、画像ファイルのサイズを自動で取得して返します。

Gulp タスクの概要は以下のとおり。

gulpfile.js
const path = require('path');
const { src, dest } = require('gulp');
const gulpPug = require('gulp-pug');
const gulpData = require('gulp-data');
const imageSize = require('image-size');

const sourcePath = path.resolve(__dirname, 'src');

exports.default = function () {
  return src(path.resolve(sourcePath, '**/*.pug'))
    .pipe(
      gulpData((file) => {
        // file: 処理中のPugファイルに関する情報
        // file.dirname: 処理中のPugファイルのあるディレクトリパス
        return {
          // imageSizeという名前の関数をPug内で使えるようにする
          imageSize: (src) => {
            // <img>タグのsrc属性のパスを基にファイルパスを生成する
            const filePath = src.startsWith('/')
              ? path.resolve(sourcePath, src.slice(1)) // /から始まるルート相対パスの場合
              : path.resolve(file.dirname, src); // 相対パスの場合
            // ファイルパスに該当する画像ファイルのサイズをimage-sizeで取得し、返す
            return imageSize(filePath);
          },
        };
      })
    )
    .pipe(gulpPug())
    .pipe(dest(path.resolve(__dirname, 'public')));
};

実際のソースコード

そうすることで、Pug 上ではimageSize関数を使い画像パスを指定するだけでサイズを取得できます。

img.pug
-
  const size = imageSize(src) // gulp-dataで定義したimageSize関数
  const width = size.width
  const height = size.height

img(src=src, alt=alt, width=width, height=height)

実際のソースコード

また、PC と SP でアスペクト比の異なる画像を切り替えるとき、<img>タグにwidth,height属性を付与しただけではスマホ表示時に SP 用画像が PC のアスペクト比で表示されてしまい表示が崩れてしまいますが、そうならないように<source>タグに SP 画像サイズのwidth,height属性を付与することで、スマホでも正しいアスペクト比で画像を表示できます。

参考: source 要素に width/height 属性を指定して各画像のアスペクト比の維持と CLS の改善を図る

オプションでwidth,heightを指定した場合はそちらが優先されるので、元の画像サイズとは異なるサイズを指定することもできます。

無効化

また、WYSIWYG 内の画像など一部の画像のみwidth,height属性の付与を無効にするには、+imgミックスインのisDisableSizeオプションの値をtrueにします。

+img({ src: 'example.png', isDisableSize: true })

参考:レイアウトシフトについて

解像度の切り替え(srcset属性)

srcset属性を使うとデバイスピクセル比によって描画する画像ファイルを変更できますが、それも自動化しています。

+imgミックスインのsrcオプションでは等倍(1x)用画像のみ指定するだけです。
パス内の/1x/の文字列を/2x/に置き換え、さらに画像アセットで 2 倍サイズの画像に付与される@2xの文字の入ったパスの画像ファイルが存在すれば、自動でsrcset属性を付与して出力します。(存在しなければタグが出力されないだけでエラーにはなりません)

Node.js
{
  // 指定したパスの画像ファイルが存在するかどうか
  detectExistImage: (srcPath) => {
    // image-sizeを流用し、置換したパスのファイルが正常に読み込めたらtrue、存在せずにエラーになったらfalseを返す
    try {
      imageSize(path.resolve(file.dirname, srcPath))
      return true
    } catch (error) {
      return false
    }
  },
}

実際のソースコード

img.pug
-
  const src2x = src.replace('/1x/', '/2x/').replace(/(\.(png|jpg|jpeg|gif))$/, '@2x$1')
  const isSrc2x = detectExistImage(src2x)
  const srcset = isSrc2x
    ? `${srcFull} 1x,${src2xFull} 2x`
    : null
  attributes.srcset = srcset

実際のソースコード

無効化

CMS から出力する画像など一部の画像のみ無効にするには、+imgミックスインのisDisableAutoDetectオプションの値をtrueにします。(※media属性も無効になります。)

+img({ src: 'example.png', isDisableAutoDetect: true })

アートディレクション(media属性)

PC/SP 画像は<source>タグのmedia属性によって切り替わります。
<source>で切り替えることで、SP 表示のときは SP 用画像しか読み込まない(PC 用画像は読み込まれない)ので、読み込み速度改善に繋がります。

+imgミックスインのsrcオプションではPC 用画像のみ指定するだけでよくsrcset属性同様、パス内の/pc/の文字列を/sp/に置き換えたパスの画像ファイルが存在すれば、自動で SP 用のmedia属性の付いた<source>タグが出力されます。

無効化

CMS から出力する画像など一部の画像のみ無効にするには、+imgミックスインのisDisableAutoDetectオプションの値をtrueにします。(※srcset属性も無効になります。)

+img({ src: 'example.png', isDisableAutoDetect: true })

また、PC(SP)のみ 2 倍画像を読み込まないようにしたい場合は、isDisable2xPc(isDisable2xSp)の値をtrueにします。

+img({ src: 'example.png', isDisable2xPc: true })

WebP(type="image/webp"属性)

読み込み速度改善のため、gulp-webpを使用し、srcディレクトリ配下の画像ファイルはすべて自動で WebP 形式に変換しています

ブラウザのサポート状態によって WebP と通常画像を出し分けるためのtype="image/webp"属性の付いた<source>タグも自動で出力します。
+imgミックスインのsrcオプションでは、.pngのような通常の画像拡張子を指定するだけで問題ありません(.webp拡張子を指定する必要はありません)。

無効化

+imgisWebpオプションをfalseにすると、そのタグのみ WebP を無効にして通常画像を表示できます。WebP 変換によって一部の画像の画質が落ちてしまった場合などは、個別に WebP を無効にします。

+img({ src: 'example.png', isWebp: false })

また、config/param.jsenableWebPの値を false にすると、全体の WebP 変換が無効になります。

Lazy loading(loading="lazy"属性)

デフォルトでは遅延読み込みさせるloading="lazy"属性(一部のブラウザのみサポート)が自動で付与されるようになっています。

無効化

ファーストビューの画像など一部の画像のみ無効にするには、+imgミックスインのisDisableLazyオプションの値をtrueにします。

+img({ src: 'example.png', isDisableLazy: false })

すべての自動出力を無効にした場合は、シンプルな<img>タグのみが出力されます。


このように、画像アセットと+imgミックスインを利用し、パフォーマンス改善に繋がるコードの出力を自動化することで効率化をしています。

脚注
  1. 参考:レイアウトシフトについて参照 ↩︎