画像を拡大できるようにしたいという欲求は前々からあった。この手のものでは Lightbox が有名だが、 jQuery べったりなのでいまさら使いたくない。 P_BLOG の頃は FacyZoom というやつを使っていて好きだったが、 2008 年から更新されていない。何かいいのがないかなと探していて Medium Zoom というのを見つけた。
ブログサービスの Medium の画像拡大機能のコピープラグインだ。 npm パッケージになっているので導入も楽だった。
実際のこんな感じ。
画像を拡大できるようにしたいという欲求は前々からあった。この手のものでは Lightbox が有名だが、 jQuery べったりなのでいまさら使いたくない。 P_BLOG の頃は FacyZoom というやつを使っていて好きだったが、 2008 年から更新されていない。何かいいのがないかなと探していて Medium Zoom というのを見つけた。
ブログサービスの Medium の画像拡大機能のコピープラグインだ。 npm パッケージになっているので導入も楽だった。
実際のこんな感じ。
外出自粛かつ自分自身にコロ助疑惑があったのでずっと部屋に閉じこもってサイトのデザインをいじってた。
上がこれまでで、左下が今後、右下が画像がワイドな場合のバージョン。これまで横幅 1280px 想定にして画像も横幅 1280px のサイズで表示するようにしていた。
ただしこれだと写真は見やすくても文字が読みにくい。人間の目は 1280px 繰り返し左右に移動させるのには適していないようだった( N=1 )。
そういうわけでまたまた cho45 さんのブログのレイアウトをパクって、文章は横幅短めに、写真はでかいサイズで表示するようにした。
文章部分の幅を 800px にして、画像を読み込んだときに一定の条件にマッチしたら写真の横幅を 1280px で表示するようにした。 margin-left: -250px;
してるのがミソ。横幅 800px だと大分文章は読みやすいし、でかい写真は大きく見えて便利。
@media screen and (min-width: 1422px) {
#content #main article .body > p {
img[class~="large"] {
width: $content-max-width;
max-width: $content-max-width;
margin-left: -250px;
}
}
}
JS はこんな感じのコードを書いた。
const checkImageSize = (target) => {
if (typeof target === 'undefined') {
return;
}
const width = target.naturalWidth;
const height = target.naturalHeight;
const isPhoto = RegExp('(lh3\.googleusercontent\.com|\.jpe?g$)').test(target.src);
if (width > 1279 && width > height && isPhoto) {
target.classList.add('large');
}
}
const lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.addEventListener('load', (event) => checkImageSize(event.target));
lazyImageObserver.unobserve(lazyImage);
}
});
});
const selectors = "#content article .body img, #content article .similar img";
const lazyImages = [].slice.call(document.querySelectorAll(selectors));
if ("IntersectionObserver" in window) {
for (let image of lazyImages) {
const src = image.src;
image.dataset.src = src;
if (lazyImages.indexOf(image) === 0) {
const promise = new Promise(resolve => resolve(image));
promise.then(checkImageSize).catch(setTimeout(checkImageSize, 100))
continue;
}
image.src = "";
};
lazyImages.forEach(lazyImage => {
lazyImageObserver.observe(lazyImage);
});
}
画像の読み込みが起こったタイミングにフックして画像のサイズチェックを行い、条件にマッチしたら img タグに class を追加し、 CSS のメディアクエリと合わせ技で大きく表示するようにしている。以前やった画像の遅延読み込みのコードを改良した。
その他、 HTML 5 対応が中途半端だったのでマークアップを見直して、適度に <header>
や <secition>
、 <article>
、 <footer>
を使ってマークアップし直した。 このせいだと思うけど Google Adsense の自動広告が差し込まれる位置が変わった。これまでヘッダーの下に入り込んでいたやつが <header>
と <article>
の間に入るようになった。
今後も外出禁止が続いたらひたすら自分のブログをいじってしまう予感。こういうエネルギーを仕事とか個人サービス開発とかに当てられるとよいのだろうけどなかなかそういう方向には向かない。
各記事の一枚目の画像をカバー画像とみなすようにして、関連記事にサムネイルを表示するようにしてみた。画像があるだけで記事をクリックしてみたい感が高まると思う(残念ながら Google Analytics で見る限り直帰率は改善してない)。人間は、文字だけを読むよりもイラストや画像など視覚的な情報を一緒に見ることで物事の理解度が高まると本で読んだ。自分で過去記事を読み直していても画像があると関連記事をクリックしてみたくなるし、満足感のある改修だった。
なおカバー画像の判定処理は記事本文を読み込んだ上で正規表現で調べているので多分重い(ベンチマークを取ったらタイトルを表示する処理に比べて二倍くらい遅かった)のだけど、関連記事表示部分はキャッシュしてるのでそんなに体感速度は悪化してないはず。
写真や画像があると文章だけよりも記憶に定着しやすいらしい。なのでヘッダー画像のバリエーションを増やしてみることにした。これまで自分が撮ってきた写真の中で気に入ってるもの、ヘッダー画像にちょうど良さそうなやつを 14 枚選んで、これまで表示していた門司の「平民食堂」のやつと合わせて 15 枚をランダムピックアップして表示させている。本当ならランダムではなく記事ごとにイメージに合う画像を選定すべきなのだろうけどそこまではやってない。「このブログ主はこういう写真の場所に行ったりこういう写真をいいと思ってるんだな」ということが伝われば良いと思ってる。
ランダム表示に関して、最初は JavaScript で CSS の background-image
を書き換える方式でやっていたが、 DOM の読み込み完了時に動く都合上、かくつきが出てしまう。それで画像の選定は Ruby で行って data-attribute として指定し、予め CSS にそれぞれの data-attribute に応じた background-image
を書いておくことにした。これでかくつきはほぼほぼなくなった。
<div id="header" data-background-image="heimin">
</div>
#header[data-background-image="heimin"] {
background-image: url(header-bg-heimin.jpg);
}
このほかにも CSS 周りを結構いじってて、パンくずリストが載る部分は文字が読みにくくなるのでグラデーションでシャドーを掛けてる。これまで float でやってた配置の制御を flexbox に置き換えたりもやった。今のデザインのベースは 10 年前に作ったので、当時はグラデーションなんかは CSS で実現できず横幅 1px の画像をリピートする方法を使ったが、これで画像を捨てて全て CSS で実現できそう。良い世の中になってきてる。