是否可以在下一个 js 中使用 svg 作为图像的占位符?

is it possible to use a svg as a placeholder for image in next js?

我正在使用下一个 Js Image 标签 (next/image)。是否可以导入 svg 并将其用作占位符,直到图像完成加载?

我尝试像在 img 标签中那样在标签的“onLoad”上创建一个使用状态并将其设置为真,但这不起作用。应该在图像加载之前显示的回退图像从未显示,就像图像总是被加载一样,即使它们没有出现在 html yet

这是我试过的代码:

const [hasImageLoaded, setHasImageLoaded] = useState(false);

  function handleImageLoaded(): void {
    setHasImageLoaded(true);
  }

const displayImgStyle = {
  display: hasImageLoaded ? undefined : 'none',
};

const loadingImgStyle = {
  display: hasImageLoaded ? 'none' : undefined,
};

return (
<>
  <div style={displayImgStyle}>
    <Image
      width={216}
      height={216}
      className={styles.pokemon}
      src={image}
      alt={name}
      title={name}
      onLoad={handleImageLoaded}
    />
  </div>

  <MySvg style={loadingImgStyle} />
</>
)

也许能帮到你:

const shimmer = (w: number, h: number) => `
  <svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
      <linearGradient id="g">
        <stop stop-color="#333" offset="20%" />
        <stop stop-color="#222" offset="50%" />
        <stop stop-color="#333" offset="70%" />
      </linearGradient>
    </defs>
    <rect width="${w}" height="${h}" fill="#333" />
    <rect id="r" width="${w}" height="${h}" fill="url(#g)" />
    <animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite"  />
  </svg>`

const toBase64 = (str: string) =>
  typeof window === 'undefined'
    ? Buffer.from(str).toString('base64')
    : window.btoa(str)

<Image
   width={32}
   height={32}
   alt={Img2}
   placeholder="blur"
   blurDataURL={`data:image/svg+xml;base64,${toBase64(shimmer(32, 32))}`}
   src={`IMG_LINK`}
/>

NextJS v11 及更高版本

使用 blurDataURL 属性 作为占位符,但在将 SVG 转换为 data-uri.

之前

阅读更多关于 blurDataURL

NextJS v11以下版本的解决方案

为了避免在占位符之前加载图像,您需要 to convert it to data-uri 并使用您的代码交付它:

const placeholder = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-8 -8 40 40' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-image'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'%3E%3C/circle%3E%3Cpolyline points='21 15 16 10 5 21'%3E%3C/polyline%3E%3C/svg%3E`

<img src={placeholder}/>

现在您需要获取真实图像并为此使用 Image class:

const [url, setUrl] = useState(placeholder) // use placeholder as default image, which appears instantly 

const img = new Image()
img.src = src
img.onload = () => setUrl(img.src) // callback is called when image is loaded, with setUrl we swap images

完整组件:

import './styles.css'
import {useEffect, useState} from 'react'

// to create data url use this service: https://heyallan.github.io/svg-to-data-uri/
const placeholder = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-8 -8 40 40' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-image'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'%3E%3C/circle%3E%3Cpolyline points='21 15 16 10 5 21'%3E%3C/polyline%3E%3C/svg%3E`

const Picture = ({alt, src, ...props}) => {
  const [url, setUrl] = useState(placeholder)
  useEffect(() => {
    if (!src) return
    const img = new Image()
    img.src = src
    img.onload = () => setUrl(img.src)
  }, [src])

  return <img alt={alt} src={url} {...props} />
}

const App = () => (
  <div className='App'>
    <h1>Hello CodeSandbox</h1>
    <Picture
      className='img'
      alt='Random image'
      src='https://picsum.photos/500/500'
    />
  </div>
)

☝️ 为避免名称冲突,导入具有不同名称的 NextJS Image 组件:

import NextImage from 'next/image' // instead of import Image from 'next/image'

在这里你可以找到the demo。要查看它是如何工作的,请单击刷新图标: