在NextJS中拥有后备映像的最佳方法是什么?

k2fxgqgv  于 2023-04-05  发布在  其他
关注(0)|答案(6)|浏览(205)

最近,我一直在NextJS中工作,该项目使用YoutubeAPI获取视频信息,包括缩略图URL。
全分辨率图像的缩略图URL如下所示:https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg
然而,有时YouTube无法生成全分辨率图像,在这种情况下,图像不会显示在我的网页上。
如果带有URL https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg的图像不存在,我希望使用另一个URL,如https://i.ytimg.com/vi/${videoId}/hqdefault.jpg
使用next/image处理此问题的最佳方法是什么?

dy1byipe

dy1byipe1#

您可以创建一个自定义映像组件,该组件扩展内置的next/image,并在映像加载失败时通过触发onError回调添加回退逻辑。

import React, { useState } from 'react';
import Image from 'next/image';

const ImageWithFallback = (props) => {
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(src);

    return (
        <Image
            {...rest}
            src={imgSrc}
            onError={() => {
                setImgSrc(fallbackSrc);
            }}
        />
    );
};

export default ImageWithFallback;

然后,您可以直接使用自定义组件而不是next/image,如下所示:

<ImageWithFallback
    key={videoId}
    layout="fill"
    src={`https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
    fallbackSrc={`https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`}
/>

传递key属性以触发videoId更改时的重新呈现。

qjp7pelc

qjp7pelc2#

这些答案很有帮助,但是有一种方法可以实现这一点,而不需要每次都传递key,方法是利用useEffect钩子:

useEffect(() => {
    set_imgSrc(src);
}, [src]);

此外,onError事件似乎不会对某些图像触发(我相信layout='fill'在某些情况下不会触发它),对于这些情况,我一直使用onLoadingComplete,然后检查图像的宽度是否为0

onLoadingComplete={(result) => {
    if (result.naturalWidth === 0) {  // Broken image
        set_imgSrc(fallbackSrc);
    }
}}

完整代码:

import Image from "next/image";
import { useEffect, useState } from "react";

export default function ImageFallback({ src, fallbackSrc, ...rest }) {
  const [imgSrc, set_imgSrc] = useState(src);

  useEffect(() => {
    set_imgSrc(src);
  }, [src]);

  return (
    <Image
      {...rest}
      src={imgSrc}
      onLoadingComplete={(result) => {
        if (result.naturalWidth === 0) {
          // Broken image
          set_imgSrc(fallbackSrc);
        }
      }}
      onError={() => {
        set_imgSrc(fallbackSrc);
      }}
    />
  );
}
clj7thdc

clj7thdc3#

@juliomalves已经给出了99%的答案,但是我想补充一下。在他的解决方案中更改src时有一个问题,因为图像不会更新,因为它得到的imgSrc值没有更新。这是我对他的回答的补充:

import React, { useState } from 'react';
import Image from 'next/image';

const ImageFallback = (props) => {
    
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(false);
    const [oldSrc, setOldSrc] = useState(src);
    if (oldSrc!==src)
    {
        setImgSrc(false)
        setOldSrc(src)
    }
    return (
        <Image
            {...rest}
            src={imgSrc?fallbackSrc:src}
            onError={() => {
                setImgSrc(true);
            }}
        />
    );
};

export default ImageFallback;

现在,imgSrc仅用作标志,并且跟踪src值,这有助于更改图像,即使您的图像之前已启用备用图像。

3npbholx

3npbholx4#

有@juliomalves解决方案与typescript

import React, { useState } from 'react';
import Image, { ImageProps } from 'next/image';

interface ImageWithFallbackProps extends ImageProps {
  fallbackSrc: string
}

const ImageWithFallback = (props: ImageWithFallbackProps) => {
  const { src, fallbackSrc, ...rest } = props;
  const [imgSrc, setImgSrc] = useState(src);

  return (
    <Image
      {...rest}
      src={imgSrc}
      onError={() => {
        setImgSrc(fallbackSrc);
      }}
    />
  );
};

export default ImageWithFallback
ut6juiuv

ut6juiuv5#

上面的解决方案对我都不起作用。但是,当我使用“onErrorCapture”而不是“onError”时,它起作用了!=)

vlf7wbxs

vlf7wbxs6#

这是我尝试用<Avatar.Image><Avatar.Fallback>子组件构建一个<Avatar>父组件。
首先,我构建了<Avatar.Image>组件来显示我们的头像图像。

import React, { useState } from 'react';
import Image from 'next/image';

function AvatarImage({ src, alt, className, ...props }) {
  const [imageSuccessfullyLoaded, setImageSuccessfullyLoaded] = useState(true);

  return (
    <div>
      {imageSuccessfullyLoaded && (
        <Image
          src={src}
          alt={alt}
          placeholder="empty"
          onError={() => {
            setImageSuccessfullyLoaded(false);
          }}
          fill
          {...props}
        />
      )}
    </div>
  );
}

export default AvatarImage;

一旦AvatarImage组件加载失败,我就让AvatarFallback组件填充背景。

import React from 'react';
import classNames from 'classnames';

function AvatarFallback({ children, className }) {
  return (
    <div
      className={classNames(
        'text-lg font-medium text-neutral-600 dark:text-neutral-200',
        className
      )}
    >
      {children}
    </div>
  );
}

export default AvatarFallback;

父组件名为Avatar

import React from 'react';
import classNames from 'classnames';
import AvatarImage from './AvatarImage/AvatarImage';
import AvatarFallback from './AvatarFallback/AvatarFallback';

function Avatar({ className, children }) {
  return (
    <div
      className={classNames(
        'relative flex items-center justify-center overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-600',
        className
      )}
    >
      {children}
    </div>
  );
}

Avatar.Image = AvatarImage;
Avatar.Fallback = AvatarFallback;

export default Avatar;

下面是我如何使用它的一个例子:(不要忘记修改${videoId}

<Avatar>
  <Avatar.Image src="https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg" alt="Avatar image" />
  <Avatar.Fallback>
    <Image
      src="https://i.ytimg.com/vi/${videoId}/hqdefault.jpg"
      fill
      alt="Avatar image"
    />
  </Avatar.Fallback>
</Avatar>

相关问题