Next.js 中的动态图像占位符

2025-06-04

Next.js 中的动态图像占位符

如何创建动态图像占位符?

TLDR

点击此处查看完整代码

长版本

能够立即在屏幕上看到某些内容会让应用程序感觉更快,
无论是在快速连接还是慢速连接中。

下面的 GIF 显示了用户使用慢速互联网连接加载图像时会看到的内容。

这给人的印象是我们的应用程序出了问题。

选择

我们可以使用 Next.js 中的内置占位符图像,
但在某些情况下,我们可能需要一些类似于实际图像的东西,比如封面图像。

查看此博客了解更多信息

好了一些,但还不够。占位符加载速度不够快,无法解决第一个问题。

此外,颜色的突然变化会让眼睛感觉不自然。
虽然我们可以为每张图片创建一个自定义的占位符,但我们需要这样做吗?

在这篇博文中,我将展示如何在 Next.js 中创建动态占位符图像。

以下是解决该问题的一般步骤

  1. 根据图像创建占位符元数据
  2. 从占位符元数据创建 SVG 组件
  3. 为图像和占位符创建容器
  4. 实际图像加载完成后卸载占位图像
  5. 将所有组件组合在一起
  6. Next.js 页面中的端到端集成

1. 根据图像创建占位符元数据

一个简单的方法是使用占位符

import {getPlaiceholder} from 'plaiceholder'

const placeholder = await getPlaiceholder(uri, { size: 64 })

// OR

const placeholder = await getPlaiceholder(uri)

// `size` decides how many blocks there will be
// ranges from 4 to 64
// default is 4
Enter fullscreen mode Exit fullscreen mode

getPlaiceholder返回promise具有以下属性的对象:

  • base64
  • blurhash
  • css
  • img
  • svg

对于我们的目的来说,我们只需要imgsvg属性。

2.创建 svg 组件

SVG 组件的创建方式取决于占位符元数据的创建。以下是的版本
参考。plaiceholder

为了更好地直观地展示如何创建 SVG 组件,下面是一个示例 SVG 元数据

2.a. 创建svg容器

SVG 元数据中的第一个元素是svg元素。SVG
元数据中的第二个元素是 SVG 属性。

function BlurringImage({ svg }){
  const Svg = svg[0]
  const svgProps = svg[1]

  return <Svg {...svgProps}>
    {/* TODO */}
  </Svg>
}
Enter fullscreen mode Exit fullscreen mode
rect2.b. 将s列表添加为svg子项

SVG 元数据中的第三个元素是 的列表rect,它将作为子元素呈现svg

function BlurringImage({ svg }){
  // ...
  const rectangles = svg[2]

  return <Svg {...}>
    {rectangles.map((rect) => {
      const Rect = rect[0]
      const rectProps = rect[1]

      <Rect {...rectProps} key={`${rectProps.x}${rectProps.y}`} />
    )}}
  </Svg>
}
Enter fullscreen mode Exit fullscreen mode

通过执行步骤2.a2.b,我们可以创建一个如下所示的 svg 组件:

2.c. 模糊 svg

可以对 SVG 进行模糊处理以消除像素化外观。

function BlurringImage({ svg }){
  // ...
  const svgProps = svg[1]

  return <Svg
    style={{
      ...svgProps.style,
      filter: `blur(5px)`,
    }}
  >
  {...}
  </Svg>
}
Enter fullscreen mode Exit fullscreen mode

应用步骤2.c将使 svg 看起来像这样:

注意:确保应用适当的过滤值

对于svg metadata较少的rects,结果可能看起来像这样:

3. 创建一个容器;然后添加要显示的 SVG 和图像

可以选择性地svg被包裹在另一个组件中(用于样式)。将 props 传播到下一个组件中。Image
imgImage

import Image from 'next/image'

function BlurringImage({ img }){
  // ...
  return <Container>
    <Svg {...}>
    <Image {...img} />
  </Container>

  // Create the Container in any way you want
}
Enter fullscreen mode Exit fullscreen mode

4. 实际图像加载完成后卸载占位图像

由于图片已加载,占位符组件可以卸载。
卸载可以通过使用useStateImagesonLoadingComplete回调方法来实现。

function BlurringImage({...}){
  // ...
  const [hasPlaceholder, setHasPlaceholder] = useState(true)

  return <Container>
    {hasPlaceholder && <Svg {...} />}
    <Image {...} onLoadingComplete={() => setHasPlaceholder(false)} />
  </Container>
}
Enter fullscreen mode Exit fullscreen mode

5. 将所有组件组合在一起

这是经过少量重构并具有默认 prop 值的最终自定义图像组件:

import React, {useState} from 'react'
import styled from '@emotion/styled'
import Image from 'next/image'

export function BlurringImage({
  svg: [Svg, svgProps, rectangles],
  img,
  alt,
  style,
  blurLevel = 5,
  height = undefined,
  width = undefined,
  ...props
}) {
  const [hasPlaceholder, setHasPlaceholder] = useState(true)

  return (
    <Container style={style}>
      {hasPlaceholder && (
        <Svg
          {...svgProps}
          style={{
            ...svgProps.style,
            filter: `blur(${blurLevel}px)`,
          }}
        >
          {rectangles.map(([Rect, rectProps]) => (
            <Rect {...rectProps} key={`${rectProps.x}${rectProps.y}`} />
          ))}
        </Svg>
      )}

      <Image
        {...img}
        {...props}
        height={height}
        width={width}
        alt={alt}
        onLoadingComplete={() => setHasPlaceholder(false)}
      />
    </Container>
  )
}

const Container = styled.div`
  position: relative;
  overflow: hidden;
  height: 100%;
  width: 100%;
`;
Enter fullscreen mode Exit fullscreen mode

6. NexJs 页面中的端到端集成

是时候将我们的自定义组件集成到 NextJs 应用程序中了

import {getPlaiceholder} from 'plaiceholder';
import {BlurringImage} from '../components/BlurringImage';

export default function IndexPage({img, svg}) {
  return (
    {/* <SomeHeaderComponent /> */}
    <BlurringImage
      img={img}
      svg={svg}
      layout="responsive"
      width={1200}
      height={800}
    />
  )
}

// or getServerSideProps depending on your needs
export async function getStaticProps() {
  const uri = 'https://i.imgur.com/gf3TZMr.jpeg';

  const {img, svg} = await getPlaiceholder(uri, {
    size: 64,
  });

  return {
    props: {
      img,
      svg,
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

最终结果如下:

即使在较慢的网络连接下,网页似乎也能加载得更快,并且图像的转换也显得更自然。

以下是当地灯塔的得分:

结论

通过添加动态占位图,用户的体验将得到提升,因为即时反馈会让人感觉应用程序运行速度更快。用户无需再盯着空白屏幕等待图片加载,尤其是在网速较慢的情况下。此外,由于占位图与原始图片相似,过渡效果也更加自然。

文章来源:https://dev.to/codegino/dynamic-image-placeholder-in-nextjs-5052
PREV
使用 Javascript 和 CSS 进行简单的电子邮件验证
NEXT
19个编程误区