最近,我一直在 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
?
您可以创建一个自定义图像组件,扩展内置的
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
更改的重新渲染。
这些答案很有帮助,但有一种方法可以实现这一目标,而无需每次都通过利用
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);
}}
/>
);
}
有 @juliomalves 的打字稿解决方案
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
@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 值的跟踪,这有助于更改图像,即使您之前有一个具有后备图像的图像。
以上解决方案都不适合我。但是,当我使用“onErrorCapture”而不是“onError”时,它起作用了! =)
<Image
loader={({ src }) => src}
src={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
width={500}
height={500}
alt={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
onError={(event) => {
event.target.id = "/images/no-thumbnail.jpg";
event.target.srcset = "/images/no-thumbnail.jpg";
}}
/>
onError 可以设置图片srcset。通过 image srcset nextjs image 显示图像。
这是我尝试使用
<Avatar>
和 <Avatar.Image>
子组件构建 <Avatar.Fallback>
父组件。
首先,我构建了
<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>
'use client'
import Image from 'next/image'
import React, { useState } from 'react'
const ImageToast = ({image}: any) => {
const [imgSrc, setImgSrc] = useState(false);
return (
<Image
src={imgSrc ? image : '/img/loader.jpg'}
onError={() => setImgSrc(true)}
alt = ''
fill
/>
)
}
export default ImageToast