このブログではHugoというmarkdownから静的サイトを生成するツールを使用しています。画像はmarkdownの書式どおり下記の記載をしています。

このままだとデータ容量が大きいためWebPに自動変換処理を追加しました。また画像を拡大して見られるようにLightbox対応も行いました。
なお本ブログでは下記のHugoテーマをお借りしております。こちらのテーマを利用している前提で記載していますが、基本的には他のテーマでも利用できると思います。
Hugo標準のgistショートコードをテーマ内に移行しました | Hugoブログテーマ「Salt」
WebPへの自動変換
このブログでは、HugoのRender Hook機能を使って、記事内の画像を自動的にWebPに変換しています。具体的には layouts/_default/_markup/render-image.html ファイルで以下の処理を行っています。
実装の流れ
- 画像リソースの取得:Markdownで指定した画像パスを
.Page.Resources.GetMatchで取得 - 形式判定:jpg/jpeg/png拡張子の画像のみWebP変換対象とする(ここで制限しないとgif動画とかも変換されてしまうので)
- リサイズ処理:元画像を幅1280pxにリサイズして軽量化
- WebP変換:リサイズ後の画像をWebP形式に変換
- HTML出力:
<picture>タグで画像を表示、念のためWebPを表示できないブラウザは従来形式で表示するようにしています
詳細は後述しますが、Lightbox対応のコードも下記には一部含まれています。
{{ $src := .Page.Resources.GetMatch (printf "%s*" .Destination) }}
{{ if $src }}
{{ $sourceImage := $src }}
{{ if in (slice "jpg" "jpeg" "png") $sourceImage.MediaType.SubType }}
{{ $resizedImage := $sourceImage.Resize "1280x" }}
{{ $webpImage := $resizedImage.Resize (printf "%dx%d webp" $resizedImage.Width $resizedImage.Height) }}
<a href="{{ $sourceImage.RelPermalink }}" class="glightbox">
<picture>
<source srcset="{{ $webpImage.RelPermalink }}" type="image/webp">
<img src="{{ $resizedImage.RelPermalink }}" width="{{ $resizedImage.Width }}" alt="{{ .PlainText }}">
</picture>
</a>
{{ end }}
{{ end }}
このRender Hookにより、Markdown内で  と書くだけで、Build時に自動的にWebP変換が行われます。
このブログではデプロイをCloudflare Pagesで行っています。Cloudflare PagesなどのCI/CDサービスではBuildの実行時間に制限があるため大量の画像がある場合はこの方法では対応できない可能性がありますのでご注意ください。
当初Gif動画なども変換対象に入っており変換時間でタイムアウトしたためjpg/jpeg/pngの拡張子のみを変換するように制限を掛けました。
Lightboxの導入
画像をクリックしたときにオーバーレイで拡大表示する、いわゆるLightbox機能を導入しています。ライブラリはGLightboxを使用しています。
GLightboxの読み込み
GLightboxはCDN経由で読み込んでいます。
まずCSSを layouts/partials/head-add.html に追加します。このコードはテーマの <head> 内で読み込まれます。
<!-- GLightbox CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/glightbox.min.css">
次にJavaScriptの読み込みと初期化を layouts/partials/body-add.html に追加します。こちらは </body> 直前で読み込まれます。
<!-- GLightbox JS -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/glightbox.min.js"></script>
<script>
// GLightbox初期化
window.addEventListener('DOMContentLoaded', function() {
const lightbox = GLightbox({
touchNavigation: true,
loop: true,
autoplayVideos: false,
zoomable: true,
draggable: true
});
});
</script>
初期化オプションでタッチ操作・ループ再生・ズーム・ドラッグを有効にしています。autoplayVideos は今回不要なのでオフにしています。(今のところ動画ファイルを直貼りはしていないので再生周りの動作確認はしていません)
Render Hookでの対応
前述のWebP変換で使用している layouts/_default/_markup/render-image.html 内の <a> タグにGLightbox用の属性を付与しています。
<a href="{{ $sourceImage.RelPermalink }}" class="glightbox" data-gallery="article-images" data-glightbox="description: {{ .PlainText }}">
class="glightbox":GLightboxがこのクラスを持つリンクを自動でLightbox対象として認識しますdata-gallery="article-images":同一記事内の画像をギャラリーとしてグループ化し、左右の矢印で画像を送れるようにしていますdata-glightbox="description: ...":画像のaltテキストをLightbox表示時の説明文として表示します
href にはWebP変換前の元画像を指定しているため、Lightboxで拡大した際は高解像度の原寸画像が表示されます。ここも将来的には解像度はそのままにWebP化したい。
カスタムスタイル
assets/scss/custom.scss にGLightbox用のスタイルを追加しています。
// GLightbox画像スタイル
a.glightbox {
cursor: zoom-in;
display: inline-block;
border: none;
img {
transition: opacity 0.3s ease, transform 0.3s ease;
}
&:hover img {
opacity: 0.9;
transform: scale(1.02);
}
}
// 記事コンテンツ内の画像を中央寄せ
.article__content {
picture,
a.glightbox {
display: block;
text-align: center;
margin: 1.5rem auto;
}
}
cursor: zoom-in で画像にホバーすると拡大カーソルに変わるので、クリックで拡大できることが視覚的に伝わるようにしています。ホバー時に少し透明度を下げつつ微拡大するアニメーションも入れています。
WebP変換と合わせた全体像
Render Hookの全体コードを改めて掲載します。WebP変換とLightbox対応の両方が含まれています。
{{ $src := .Page.Resources.GetMatch (printf "%s*" .Destination) }}
{{ if $src }}
{{ $sourceImage := $src }}
{{ if in (slice "jpg" "jpeg" "png") $sourceImage.MediaType.SubType }}
{{ $resizedImage := $sourceImage.Resize "1280x" }}
{{ $webpImage := $resizedImage.Resize (printf "%dx%d webp" $resizedImage.Width $resizedImage.Height) }}
<a href="{{ $sourceImage.RelPermalink }}" class="glightbox" data-gallery="article-images" data-glightbox="description: {{ .PlainText }}">
<picture>
<source srcset="{{ $webpImage.RelPermalink }}" type="image/webp">
<img src="{{ $resizedImage.RelPermalink }}" width="{{ $resizedImage.Width }}" alt="{{ .PlainText }}">
</picture>
</a>
{{ else }}
<a href="{{ $sourceImage.RelPermalink }}" class="glightbox" data-gallery="article-images" data-glightbox="description: {{ .PlainText }}">
<img src="{{ $sourceImage.RelPermalink }}" width="{{ $sourceImage.Width }}" height="{{ $sourceImage.Height }}"
alt="{{ .PlainText }}">
</a>
{{ end }}
{{ else }}
<a href="{{ .Destination }}" class="glightbox" data-gallery="article-images" data-glightbox="description: {{ .PlainText }}">
<img src="{{ .Destination }}" alt="{{ .PlainText }}">
</a>
{{ end }}
WebP変換対象外の画像(gifなど)やリソースとして取得できない外部画像に対しても、else ブロックでLightboxの <a> タグを付与しているため、すべての画像がクリックで拡大表示できるようになっています。
まとめ
これによりというmarkdownでおなじみの形式で画像を貼り付けるだけで、WebPへの変換・リサイズとLightboxでのクリック拡大等がすべて自動で挿入されるようになりました。書く方も楽ですし、見る方も便利な良い改善になりました。
Hugoにはまだまだ知らない機能が眠っていそうなので今後もいろいろ発掘しがいがありそうです。