Hexoでnative-lazy-loadingに対応する

Placeholder of missing images.

Caution

この記事はHexo(2023年4月17日以前)、またはGatsby(2024年4月13日以前)時代の記事だよ❗ 現在のブログとは見た目や機能が異なる可能性があるよ❗

フィギュアレビュー記事の表示速度が遅いので、 ブラウザnativeのlazy-loading(遅延読み込み) に対応する。 自前のscriptでloading, width, height属性を付与する作戦。

長くなっちゃったので、記事は折りたたんである↓。

手順

まず、image-sizeを使うので、npm install image-sizeしておく。

次に、↓のscriptをthemes/***/scriptsに置く。 scriptsフォルダに置いたjsファイルはhexoのgenerate時に自動で実行される。

// This script refers to `hexo-native-lazy-load`.
// <https://github.com/fengkx/hexo-native-lazy-load>

// require
const sizeOf = require("image-size");
const path = require("path");
const fs = require("fs");
const log = hexo.log;

// config
const regex = /<img[^>]+src=\"(.+?)\"[^>]*>/gm;

// function
const mylog = (...args) => {
  log.debug(path.basename(__filename), ": ", ...args);
};

const replace = (html, rootPath) => {
  return html.replace(regex, (s, relativePath) => {
    mylog('s: "' + s + '".');

    if (s.indexOf('loading="lazy"') !== -1) {
      mylog('Already added `loading="lazy"`. Do nothing.');

      return s;
    }

    const imagePath = path.join(rootPath, relativePath);
    mylog("image path: " + imagePath);

    if (rootPath !== "" && fs.existsSync(imagePath)) {
      const dimensions = sizeOf(imagePath);
      mylog("width: " + dimensions.width + ", height: " + dimensions.height);

      return (
        s.substring(0, 4) +
        ' loading="lazy"' +
        ` width="${dimensions.width}"` +
        ` height="${dimensions.height}"` +
        s.substring(4, s.length)
      );
    } else {
      mylog("Not found image.");

      return s.substring(0, 4) + ' loading="lazy"' + s.substring(4, s.length);
    }
  });
};

// main
hexo.extend.filter.register("after_post_render", function (data) {
  mylog('replace: "' + data.title + '".');
  data.content = replace(data.content, hexo.source_dir);
  return data;
});

次に、css(stylus)の修正もいる。↓のファイルをいじる。 (landscape以外を使ってる人は適宜読み替えてね。)

  • themes/landscape/source/css/_partial/article.styl

ツリー構造で示すと、↓をいじる。

  • .article-entry
    • img, video
      • max-height: 100vhの設定を無効化(コメントアウト)する。

最終的には↓のようになってる。 (max-height以外のコメントアウトは無視して。)

  img, video
    max-width: 100%
    // max-height: 100vh
    // width: auto
    height: auto
    // object-fit: contain
    display: block
    margin: auto

最後に、clean&generateすればOK。

解説と蛇足

jsの中身は hexo-native-lazy-load pluginのほぼパクり。

hexo-native-lazy-load pluginそのままだと、 post_asset__folderを有効にしないと、うまく動作しない。 <img>widthheightを入力してくれない。

この記事のjsはpost_asset_folderが無効でも動作するように改良した(つもり)。 やってることは、大雑把に↓の2つ。

  1. loading="lazy"を付与
  2. image-sizeで画像サイズ取得して、width, height属性を付与

ちなみに、pluginとして公開する予定はなし。体裁を整えるのがめんどいため。

cssについては、本当はmax-height: 100vhの設定も有効のままにしておきたかった。 だけど、lazy-loadingで有効にすると、aspect比が崩れちゃうのよね…。 なんでaspect比を固定する属性とかがcssにないんだろう。不思議。

なお、lazy-loadingが効いてるかどうかは、chromeのデバッガを使えば分かる。 Networkタブで、スクロールするごとに画像がloadされてれば成功。 ↓の例だと、prisma_klangfest_chloe_3.webpが遅延読み込みした画像。

chromeでlazy-loadingの動作確認

他、 先駆者の方の記事 も参考にしてる。 感謝感謝。

これで、フィギュアレビュー記事の体感表示速度が上がったはず。 Netlifyくん、転送速度が結構遅い。 無料のホスティングサービスなのでしょうがない。工夫でカバー。