本文永久リンク次の js で画像のぼかしプレースホルダーを追加する方法 | トラビスのブログ
本文の内容はHow to: Blurred images on load in Next.jsから取得しました。コードの変更は私のこのコミットで行われました。
紹介#
Next.js の Image コンポーネントは、blurDataURL パラメータをサポートしており、画像が完全に読み込まれる前にベース 64 の画像をプレースホルダーとして使用することができます。
画像の位置を Markdown 構文で制御し、画像のぼかし生成プロセスをサーバーのビルド時に行い、フロントエンドで画像を取得してから生成するのではなく、生成プロセスを画像コンポーネントに含めることはできません。そのため、mdx が tsx オブジェクトにレンダリングされる際に行う必要があります。このプロセスを制御するために、rehype プラグイン1を使用しました。
mdx ファイルは contentlayer によって管理され、まず remark を使用して mdx を html に変換し、その後 rehype を使用していくつかの後処理を行い、tsx に変換してレンダリングします。この一連のプロセスはnext build
の段階で完了し、要件を満たしています。
開始#
- 必要なパッケージをインストールします。
plaiceholder
は画像から対応するぼかしプレースホルダーを生成するパッケージで、直接ベース 64 形式で生成することができます。
pnpm i plaiceholder unist-util-visit
- 次に、対応する img タグを見つけ、対応する src ファイルを読み取り、plaiceholder を使用して対応するベース 64 文字列を生成し、img の blurDataURL 属性に配置し、その後、独自のコンポーネントでパラメータを処理します。
import { getPlaiceholder } from "plaiceholder";
import { visit } from "unist-util-visit";
// ノードが画像ノードであるかどうかを確認するだけの関数
function isImageNode(node) {
const img = node;
return (
img.type === "element" &&
img.tagName === "img" &&
img.properties &&
typeof img.properties.src === "string"
);
}
// ぼかし画像に使用する指定された`src`のプロパティを返す
export async function returnProps(src) {
const { base64: blurDataURL, img } = await getPlaiceholder(src);
// 解像度の計算中にエラーが発生した場合はエラーをスローする
if (!img) throw Error(`Invalid image with src "${src}"`);
return {
blurDataURL,
};
}
async function addProps(node) {
// 画像の新しいプロパティを返す
const { blurDataURL } = await returnProps(node.properties.src);
node.properties.blurDataURL = blurDataURL;
}
const imageMetadata = () => {
return async function transformer(tree) {
// マークダウンファイルからすべての画像を保持する配列を作成する
const images = [];
visit(tree, "element", (node) => {
// ツリー内のすべてのノードを訪れ、画像であるかどうかをチェックし、画像をimages配列に追加する
if (isImageNode(node)) {
images.push(node);
}
});
for (const image of images) {
// すべての画像をループしてプロパティを追加する
await addProps(image);
}
return tree;
};
};
export default imageMetadata;
- img タグを処理し、img を Next.js の
Image
に変換します。
import Image from "next/image";
export default function MyImage({
src,
width,
height,
alt,
blurDataURL,
}: any) {
return (
<div className="not-prose mx-2 mt-4 mb-0 break-inside-avoid-page">
<div>
<Image
src={`${src}`}
width={width}
height={height}
alt={alt}
blurDataURL={blurDataURL}
placeholder="blur"
/>
</div>
</div>
);
}
- contentlayer の設定に作成した rehype プラグインを追加します。
import imageMetadata from "./lib/imageMetadata";
...
export default makeSource({
contentDirPath: "data",
documentTypes: [Blog, Movie, About, Idea],
mdx: {
rehypePlugins: [
[rehypeImgSize, { dir: "public" }],
rehypeCodeTitles,
imageMetadata,
[rehypePrism, { showLineNumbers: true }],
rehypeKatex,
rehypeSlug,
],
},
});
以上です。