最近,我一直在NextJS中做一个项目,该项目使用YoutubeAPI来获取视频信息,包括缩略图URL。
全分辨率图像的缩略图 URL 如下所示:https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg
但是,有时YouTube无法生成全分辨率图像,在这种情况下,图像不会显示在我的网页上。
如果带有 URLhttps://i.ytimg.com/vi/${videoId}/maxresdefault.jpg
的图像不存在,我希望使用另一个 URL,例如https://i.ytimg.com/vi/${videoId}/hqdefault.jpg
用next/image
处理这个问题的最佳方法是什么?
您可以创建自定义映像组件,该组件通过触发onError
回调来扩展内置next/image
并在映像加载失败时添加回退逻辑。
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给出了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 值的跟踪,即使您之前有一个具有回退图像的图像,这也有助于更改图像。
打字稿是@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
以上解决方案都不适合我。但是,当我使用"onErrorCapture"而不是"onError"时,它有效!=)
这是我尝试使用<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>