備忘録的なもの

Hugo に baguetteBox.js を埋め込む

ブログ記事内の画像を見やすくするために、 baguetteBox.js を使って画像を表示するようにする。

背景

このブログは Hugo で生成していて、記事内で画像を使う際は Markdown で以下のような感じで埋め込んでいた。

![サンプル画像](images/image1.webp)

この場合、ブログ上での見え方としてはこんな感じで見える。(サンプルとして Hugo 公式のトップページ)

サンプル画像

見ての通り、記事エリアの幅などの関係で画面キャプチャとかを貼ると大体の場合縮小されるのだが、 拡大して見るにも別タブで開いて見る必要があったりしてイマイチ使い辛い。

ブログでよく見るクリックするとポップアップして拡大表示される感じにしたかったので、手を加える事にした。

よく見るやつは Lightbox というらしいことは分かったが、 ひとまず似たようなモノが無いかも軽く調べた。 Lightbox 系と呼ばれている jQuery のプラグインが多かったが、jQuery への依存なしで動くものもいくつかあった。 この画像表示のためだけに jQuery 入れるのも大げさではと思ったり、調べてる中で jQuery 時代遅れ論とかもいくつか目に入ったりしたので、 今回は単体で動くものを使うことにした。

単体で動くものとしては以下の3つ辺りが見つかったが、 今回はこの内 baguetteBox.js を使うことにした。

選んだ基準としては、単純に GitHub のスター数が多かったのと、 デモページが見やすくて参考にしやすそうだったため。

作業

使い方は GitHub の README に書いているのでそれを参考に進める。

baguetteBox.js を使って画像を表示するための処理としてはざっくり3段階に分けられる。

  1. JavaScript および CSS ファイルの読み込み
  2. 記事を書く際に baguetteBox.js で表示するための形式で HTML を記述する
  3. baguetteBox.js 部分の初期化処理の実行

この内、 1 と 3 に関しては Hugo のレイアウトファイルの修正、 2 に関しては baguetteBox.js 用のHTML形式に変換する shortcode を定義して記事を書く際に使うことにした。

Hogo レイアウトファイルの変更

まず、今使っている mainroad テーマの場合、手を加えるレイアウトファイルは themes/mainroad/layouts/_default/baseof.html になるので、これを弄っていく

レイアウトファイルをコピー

cd <Hugoプロジェクトのルート>
mkdir -p layouts/_default/
cp -i themes/mainroad/layouts/_default/baseof.html layouts/_default/baseof.html

コピーした先のレイアウトファイルを変更(以下はコピー元ファイルとの diff )

--- themes/mainroad/layouts/_default/baseof.html        2022-08-31 11:27:29.125547891 +0900
+++ layouts/_default/baseof.html        2022-10-25 16:51:20.093361227 +0900
@@ -43,6 +43,10 @@
                {{ template "_internal/google_analytics_async.html" . }}
                {{- end }}
        {{- end }}
+
+       {{- if .HasShortcode "baguettebox" }}
+               <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.css" integrity="sha512-NVt7pmp5f+3eWRPO1h4A1gCf4opn4r5z2wS1mi7AaVcTzE9wDJ6RzMqSygjDzYHLp+mAJ2/qzXXDHar6IQwddQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
+       {{- end }}
 </head>
 <body class="body">
        <div class="container container--outer">
@@ -66,5 +70,13 @@
 <script src="{{ . | relURL }}"></script>
 {{- end }}
 {{- partial "mathjax.html" . -}}
+{{- if .HasShortcode "baguettebox" }}
+       <script src="https://cdnjs.cloudflare.com/ajax/libs/baguettebox.js/1.11.1/baguetteBox.min.js" integrity="sha512-7KzSt4AJ9bLchXCRllnyYUDjfhO2IFEWSa+a5/3kPGQbr+swRTorHQfyADAhSlVHCs1bpFdB1447ZRzFyiiXsg==" crossorigin="anonymous" referrerpolicy="no-referrer" async></script>
+       <script>
+               window.addEventListener('load', function() {
+                       baguetteBox.run('.baguettebox-gallery');
+               });
+       </script>
+{{- end }}
 </body>
-</html>
\ No newline at end of file
+</html>

意識したポイントとしては以下辺り

  • 後述の baguetteBox.js を使うための shortcode が記事内で使わている場合のみ JavaScript や CSS を読み込むようにしている
  • ファイルの読み込みに関しては CDN ( cdnjs ) からのみ行う様にしている
    • 理由としては以下
      • 大したサイズでは無いが GitHub Pages のリソース(閲覧時の通信量、リポジトリ容量)を節約できる
      • ローカルファイル読むより今っぽい気がした
    • CDN がダメならローカルファイル、みたいな実装もできそうだったが今回はしていない
  • <link> <script> の部分はほぼ cdnjs からコピーしてきたまま
  • JavaSript の読み込み位置は諸説ありそうだがデモページを参考にして <body> の最後にした
    • CSS の読み込みは <head> の最後、JavaScript の読み込みと初期化処理は <body> の最後
  • 初期化処理をどう組み込むかが難しかったため、画像のグループ化は一旦使わないことにした
    • そのため baguetteBox.js で使う class 名は固定値( .baguettebox-gallery )にしている

記事書く時用の Shortcode を作成

上記作業が済んだ段階で、記事の Markdown に HTML タグをベタ書きすれば baguetteBox.js での表示はできるようにはなっているのだが、 毎回 HTML のタグを全部書くのも手間なので Shortcode を作成する。 (将来的に「やっぱり Lightbox とか Luminous とか別のものを使おう」となった場合にラップしていた方が置換が楽そう、という思惑もある)

まず shortcode の定義ファイルを置くためのディレクトリを作成

cd <Hugoプロジェクトのルート>
mkdir -p layouts/shortcodes/

baguetteBox.js のデモページ も参考にしながら、 shortcode 用の HTML ファイル layouts/shortcodes/baguettebox.htmlをこんな感じで作成

{{- $src := .Get "src" -}}
{{- $alt := .Get "alt" -}}
{{- $caption := .Get "caption" -}}
{{- $thumb := .Get "thumb" -}}
{{- if or ( not $src ) ( not $alt ) -}}
  {{ errorf "missing value of params 'src' or 'alt' : %s" .Position }}
{{- end -}}
{{- if not $thumb -}}
  {{- $thumb = $src -}}
{{- end -}}
<div class="baguettebox-gallery">
  <a href="{{ $src }}" data-caption="{{ $caption }}">
    <img src="{{ $thumb }}" alt="{{ $alt }}">
  </a>
</div>

作業としては以上で、あとは記事を Markdown で書く際に、以下の様な形で画像を差し込む様にする。

{{< baguettebox src="画像へのパス" alt="代替テキスト" thumb="サムネイル画像へのパス" caption="画像のキャプション" >}}

shortcode の通りではあるが、 srcalt は必須、 thumbcaption は省略可にしている

動作例

諸事情( 追記 参照)により今は動かないため、動く設定にしていた時の画像

動作例1

srcalt のみ指定。

{{< baguettebox src="image2.webp" alt="動作例1" >}}
動作例1
baguetteBox.js で画像を表示している様子その1

動作例2

thumbcaption も指定

{{< baguettebox src="image3.webp" alt="動作例2" thumb="image3_thumb.webp" caption="以前も使った池袋で撮った写真" >}}
動作例2
baguetteBox.js で画像を表示している様子その2

裏の記事内ではちゃんとサムネイルになっていて、(コレPCで見た人気付くんだろうかという気もするが)ちゃんとキャプションも付いている。

補足

JavaScript 、 CSS の読み込みに使う CDN について

今回 JavaScript や CSS の読み込みに CDN を使ったが、 cdnjs と jsDelivr のどっちが良いんだろうかというところで少し調べたメモ。

比較には CDNPerf を見るのが手っ取り早いらしい、ということで見ると以下の感じ。

CDNPerfでの比較

コレを見る限り 2022/10/28 時点では速度的には cdnjs (Cloudflare CDN)、安定性は jsDelivr のようだった。

この他、2016/03/06 の記事ではあるが、実際にライブラリごとにダウンロードして時間を測ってまとめてる方も居て、 これだと jsDeliver の方が速度的には良い結果が出ていた。

計測方法の違いによるものなのか、数年の間で入れ替わったのかは分からないが、 どちらかが極端に良い、悪いとかはなさそうなので正直どっちでも良さそう。

そもそも今回使うものは JavaScript、CSS ともに小さいのでそんな気にするものでもない、という説はある。

あとがき

どうしようか迷いつつエイヤで決めた部分はいくつかあるが、ひとまず最低限動いてよかった。

  • 今回の実装だと画像のグループ化ができない
    • 記事エリア全体を <div class="baguettebox-gallary"> で囲ってしまえば、記事に貼った全画像を1つのグループとして見せることができそうではあったが書き方としてどうなんだろうかと思ったので今回は避けた
      • 何も問題ないかもしれないが、その辺を判断するための理解が不足している
  • 一応サムネイルも指定できるようにはしたが、あんまり使わない気がする
    • 写真なら元々縮小したりするのでついでにサムネイルを作ったりも手間無いかもしれないが、画面キャプチャとかは作るのが面倒になる気がする
    • とはいえサムネイル指定を省略するとおそらくほとんどの画像が横幅いっぱいになり、若干画像の圧が強くなるので何か考えたい
      • スクリプトか何かでサムネイルをまとめて生成する仕組みを作る
      • baguetteBox.js を使う部分の CSS に手を加えて、多少小さく表示するようにする
  • baguetteBox.js のバージョン追従
    • バージョン履歴見る限りそんな頻繁に上がるものではなさそうだが、何もしないとおそらくバージョンが上がった場合気付かない
    • これは baguetteBox.js に限った話でも無いので、方針考えても良いかもしれない

また、今回 Hugo の機能として以下辺りを初めて弄ったが、 触りとして丁度良い感じの分量だったので Hugo の理解を深めるという意味でも良かった。

  • レイアウトテンプレート
  • shortcode

あとは気づいたタイミングで適宜調整していこうと思う

参考リンク

追記) 2022/11/08

この記事では baguetteBox.js を使ったが、この記事を書いた後で Hugo が標準で持っている画像処理機能を使って サムネイル生成、記事内への画像挿入を行う様に変更している。

そちらの実装については以下記事でまとめた。

上記作業に伴って今回実装した部分を削除したため、この記事の実行例のところは動いている様子の画像に差し替えた。