Travis

Travis

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

在 nextjs 中添加图像的模糊 placeholder

本文永久链接在 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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。