やりたいこと 本ブログでは表示速度を優先し(?)、最低限の解像度を持つ画像をレスポンシブに配信しています。
しかしながら特にスマホにおいて、画像を拡大するとその粗さが気になりだしました。
そこでタイトルの通り、photoswipe
を導入し、画像がクリックされた際に高解像度の画像のスライドショーが表示されるようにしてみました。
とにかくはじめます🍛
photoswipe 今回はphotoswipe
と呼ばれるJavascript製のライブラリを使用します。
何ができるかは公式サイトを見れば良く分かります。
https://photoswipe.com/
一部条件はありますが、基本的にはMITライセンスとして提供されています。
The script is free to use for personal and commercial projects. It falls under the MIT license with one exception: Do not create a public WordPress plugin based on it, as I will develop it.
同様の機能を提供するライブラリとしては fancybox が有名です。
ただしこちらは jQuery 必須であり、パフォーマンスの懸念があったため採用は見送りました。
導入 基本的にはこちら の手順通りで問題ないはずです。
ライブラリのインストール 私はnpmを利用たのでこれだけ。
$ npm install photoswipe --save
Hugoの設定 imgタグ 基本的にはphotoswipe
化したい画像に対して、data-size
属性で画像サイズを格納しているだけです。
以下のimage.html
は私が独自に作成している画像出力用のテンプレートですが、大まかなイメージは以下の通りです。
image.html {{ $imgSrc := $image.RelPermalink }}
{{ $imgWidth := $image.Width }}
{{ $imgHeight := $image.Height }}
<img src = "{{ $imgSrc }}" data-size = "{{ printf " % dx % d " $ imgWidth $ imgHeight }}" />
実際にはレスポンシブ対応などが入っていますが、簡略化しています。
表示用DOMの作成 phtoswipe
によるギャラリー画像を表示するためのコンテナを用意します。
phtoswipe
に自動的に作成してほしいことろですが、あえてユーザに任せることでライブラリのサイズを小さく保ち、かつ柔軟なカスタマイズを可能にしているようです。
実際には公式のテンプレートをそのままコピペでも十分です。
photoswipe.html <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class = "pswp" tabindex = "-1" role = "dialog" aria-hidden = "true" >
<!-- Background of PhotoSwipe.
It's a separate element as animating opacity is faster than rgba(). -->
<div class = "pswp__bg" ></div >
<!-- Slides wrapper with overflow:hidden. -->
<div class = "pswp__scroll-wrap" >
<!-- Container that holds slides.
PhotoSwipe keeps only 3 of them in the DOM to save memory.
Don't modify these 3 pswp__item elements, data is added later on. -->
<div class = "pswp__container" >
<div class = "pswp__item" ></div >
<div class = "pswp__item" ></div >
<div class = "pswp__item" ></div >
</div >
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class = "pswp__ui pswp__ui--hidden" >
<div class = "pswp__top-bar" >
<!-- Controls are self-explanatory. Order can be changed. -->
<div class = "pswp__counter" ></div >
<button class = "pswp__button pswp__button--close" title = "Close (Esc)" ></button >
<button class = "pswp__button pswp__button--share" title = "Share" ></button >
<button class = "pswp__button pswp__button--fs" title = "Toggle fullscreen" ></button >
<button class = "pswp__button pswp__button--zoom" title = "Zoom in/out" ></button >
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class = "pswp__preloader" >
<div class = "pswp__preloader__icn" >
<div class = "pswp__preloader__cut" >
<div class = "pswp__preloader__donut" ></div >
</div >
</div >
</div >
</div >
<div class = "pswp__share-modal pswp__share-modal--hidden pswp__single-tap" >
<div class = "pswp__share-tooltip" ></div >
</div >
<button class = "pswp__button pswp__button--arrow--left" title = "Previous (arrow left)" >
</button >
<button class = "pswp__button pswp__button--arrow--right" title = "Next (arrow right)" >
</button >
<div class = "pswp__caption" >
<div class = "pswp__caption__center" ></div >
</div >
</div >
</div >
</div >
このテンプレートを/layouts/_default/single.html
に差し込んでおきました。
/layouts/_default/single.html {{ define "main" }}
:
略
{{ partial "photoswipe.html" . }}
{{ end }}
ちなみにsingle.html
はHugoでは記事に適用されるテンプレートファイルです。
つまりTop画面や一覧画面ではphotoswipe
は利用しない方針としました。
javascript javascriptにより画像がクリックされた際にギャラリーが表示されるようにします。
処理内容はコードのコメントに記載した通りです。
pswp.js import 'photoswipe/dist/photoswipe.css'
import 'photoswipe/dist/default-skin/default-skin.css'
import PhotoSwipe from 'photoswipe/dist/photoswipe.min.js'
import PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default.min.js'
const setup = () => {
// 画像リストを作成
const items = Array.from (document.querySelectorAll ('img[data-size]' )).map (i => {
const wh = i .getAttribute ('data-size' ).split ('x' )
return {
src : i .getAttribute ('src' ),
w : parseInt(wh [0 ], 10 ),
h : parseInt(wh [1 ], 10 )
}
})
// photoswipe.htmlで定義したルート
const pswpElement = document.querySelector ('.pswp' )
// 画像クリック時にギャラリーが表示されるようにする
document.querySelectorAll ('img[data-size]' ).forEach ((e , i ) => {
e .onclick = () => {
const gallery = new PhotoSwipe (pswpElement , PhotoSwipeUI_Default , items , {
shareEl : false ,
index : i
})
gallery .init ()
}
})
}
setup ()
結果 実際にクリックしてみてください。
パフォーマンス改善 ちょっと気になるのはphotoswipe
自体のモジュールサイズです。
導入前から比較した、追加容量はだいたい以下でした。(gzip圧縮前)
type size js +42K css +10K image +3K
jQueryなどに比べるともちろん容量は少ないですが、それでもやはりこの増加量は気になります。
少しでも減らすためにcssに関しては未使用のスタイル(sns表示とコメント表示)を PurgecssPlugin により削っていますが、せいぜい2Kほどしか削減できませんでした。
サイズを減らす以外にもっと有効な方法としては、遅延ローディングがあります。
要するにphotoswipe
は画像をクリックした際に必要となるだけで、ブログの記事を表示するうえでは全く役に立ちません。
言い換えると、画像がクリックされて初めて上記のモジュールが必要となります。
よってphotoswipe
のモジュールは、画像が初めてクリックされた際にダウンロードされるように修正します。
と言っても主要な変更はこれぐらいです。
photoswipe.html :
略
{{ $scripts := resources.Get "output/js/pswp.js" | fingerprint }}
{{ with $scripts }}
<script type = "text/javascript" >
document.querySelectorAll ('img[data-size]' ).forEach (img => {
img .style .cursor = 'pointer'
img .onclick = (event ) => {
// 初回クリックでモジュールをダウンロード
import ("{{ .Permalink | relURL }}" ).then (() => {
// ダウンロード完了後改めてクリック
event .target .click ()
})
}
//-> このonclickはpswp.jsロード後に上書きされる
})
</script >
{{end}}
開発者ツールで確認 記事の表示直後の状態では、縮小された画像がダウンロードされています(赤点線)
そして画像をクリックした際に、オリジナル画像およびphotoswipe
のモジュールがダウンロードされます(赤点線)
pswp.xxxx
はphotoswipe
関連のモジュール理論的にはjs(42K)+css(10K)=52K
になるはずですが、webpackによりバンドル化しているので若干オーバヘッド分が含まれます default-skin.svg
はcssから参照されている画像まとめ photoswipe
を導入して、画像クリック時に高解像度の画像ギャラリーが表示されるようにました。
またダイナミックローディングにより画像をクリックしない限り、これまでとほぼ変わらないのでパフォーマンスの劣化も問題ありません。
問題はそこまで高解像度で表示したい画像が今のところない事です。