Travis

Travis

这里支持的小组件太少,写不下😂,欢迎查看我的个人 blog <blog.lxythan2lxy.cn>

在 nextjs 中添加图像的模糊占位符

本文永久鏈接在 nextjs 中添加圖像的模糊 placeholder | Travis' Blog

本文內容來自 How to: Blurred images on load in Next.js,修改程式碼在我的這個提交

介紹#

Next.js 的 Image 元件支援 blurDataURL 參數,可以在圖片完全載入之前先使用 base64 的圖片來充當佔位符。

為了可以在 mdx 中用 markdown 語法控制圖片位置,並且模糊圖片的生成過程應該是在伺服器 build 時生成的,而不是前端 Get 到圖片之後再生成,我們不能把生成過程寫在圖片元件當中。
而應該在 mdx 渲染到 tsx 物件時進行,為了控制這個過程,我使用了 rehype 插件1

我的 mdx 檔案,由 contentlayer 負責管理,先用 remark 從 mdx 轉換為 html,再使用 rehype 進行一些後處理,轉換為 tsx 進行渲染,這一系列流程都是在next build階段完成的,恰好滿足了需求。

開始#

  1. 首先安裝下所需要用的包,plaiceholder 是一個可以利用圖片生成對應模糊佔位圖的包,可以直接生成 base64 格式。
pnpm i plaiceholder unist-util-visit
  1. 接下來只需要找到對應的 img 標籤,讀取對應的 src 檔案,然後通過 pliceholder 生成對應的 base64 字串,放入 img 的 blurDataURL 屬性當中,接下來用我們自己的 Component 處理一下參數就可以了。
import { getPlaiceholder } from "plaiceholder";
import { visit } from "unist-util-visit";

// Just to check if the node is an image node
function isImageNode(node) {
  const img = node;
  return (
    img.type === "element" &&
    img.tagName === "img" &&
    img.properties &&
    typeof img.properties.src === "string"
  );
}

// Returns the props of given `src` to use for blurred images
export async function returnProps(src) {
  const { base64: blurDataURL, img } = await getPlaiceholder(src);

  // If an error happened calculating the resolution, throw an error
  if (!img) throw Error(`Invalid image with src "${src}"`);

  return {
    blurDataURL,
  };
}

async function addProps(node) {
  // return the new props we'll need for our image
  const { blurDataURL } = await returnProps(node.properties.src);

  node.properties.blurDataURL = blurDataURL;
}

const imageMetadata = () => {
  return async function transformer(tree) {
    // Create an array to hold all of the images from the markdown file
    const images = [];

    visit(tree, "element", (node) => {
      // Visit every node in the tree, check if it's an image and push it in the images array
      if (isImageNode(node)) {
        images.push(node);
      }
    });

    for (const image of images) {
      // Loop through all of the images and add their props
      await addProps(image);
    }

    return tree;
  };
};

export default imageMetadata;
  1. 處理一下 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>
  );
}
  1. 在 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,
    ],
  },
});

OK,到這裡就完成了。

Footnotes#

  1. Github rehypejs/rehype

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。