NextJS 提供了一个内置的图片组件,它具有许多有用的功能,我们可以利用这些功能并添加一些自定义样式来创建一个带有模糊加载效果的漂亮图片。
NOTE
以下所有示例都使用 React 和 TypeScript 编写,样式采用 Tailwind CSS
模糊图片
这里的简单思路是先让图片模糊(使用 blur-xl
类),然后当图片加载完成时通过移除模糊效果(使用 blur-0
)来实现淡入效果。
'use client'
import { clsx } from 'clsx'
import type { ImageProps as NextImageProps } from 'next/image'
import NextImage from 'next/image'
import { useState } from 'react'
export interface ImageProps extends Omit<NextImageProps, 'src' | 'priority'> {
src: string
}
export function Image(props: ImageProps) {
let { alt, src, loading = 'lazy', style, className, ...rest } = props
let [loaded, setLoaded] = useState(false)
return (
<div
className={clsx(
// Add a `image-container` class to the parent element
// to make it easier to adjust the styles in mdx file content
'image-container overflow-hidden',
!loaded && 'animate-pulse [animation-duration:4s]',
className
)}
>
<NextImage
className={clsx(
'[transition:filter_500ms_cubic-bezier(.4,0,.2,1)]',
'h-full max-h-full w-full object-center',
loaded ? 'blur-0' : 'blur-xl'
)}
src={src}
alt={alt}
style={{ objectFit: 'cover', ...style }}
loading={loading}
priority={loading === 'eager'}
quality={100}
onLoad={() => setLoaded(true)}
{...rest}
/>
</div>
)
}
我使用了 Tailwind 的 模糊滤镜 工具类来创建模糊效果。 你可以通过将 blur
工具类与其他类如 灰度、缩放 等组合使用来创建自己的变体。 (记得同时更新 transition
属性)。
调整尺寸
该组件会自动根据子图片调整大小,你可以通过传入 className
来自定义其尺寸。 例如:
<Image
src={logo}
alt={org}
className="h-12 w-12 shrink-0 rounded-md"
style={{ objectFit: 'contain' }}
width={200}
height={200}
/>
MDX 支持
如果你想在 MDX 文件中使用这个组件来渲染图片,你需要更新 tailwind typography 配置来让图片具有响应式效果。
module.exports = {
theme: {
extend: {
typography: ({ theme }) => ({
DEFAULT: {
css: {
'.image-container': {
width: 'fit-content',
marginLeft: 'auto',
marginRight: 'auto',
img: {
marginTop: 0,
marginBottom: 0,
},
},
// ... more typography styles
},
},
}),
},
},
}
避免每次渲染时都触发模糊效果
每次渲染 Image
组件时都会触发模糊效果(即使图片已经加载完成)。 如果你想避免这种情况,你需要手动控制 loaded
状态:
'use client'
import { clsx } from 'clsx'
import type { ImageProps as NextImageProps } from 'next/image'
import NextImage from 'next/image'
import { usePathname } from 'next/navigation'
import { useState } from 'react'
let loadedImages: string[] = []
// 检测图片是否已经加载过,以避免在每次组件渲染时
// 基于路由路径重复触发模糊效果
function useImageLoadedState(src: string) {
let pathname = usePathname()
let uniqueImagePath = pathname + '__' + src
let [loaded, setLoaded] = useState(() => loadedImages.includes(uniqueImagePath))
return [
loaded,
() => {
if (loaded) return
loadedImages.push(uniqueImagePath)
setLoaded(true)
},
] as const
}
export interface ImageProps extends Omit<NextImageProps, 'src' | 'priority'> {
src: string
}
export function Image(props: ImageProps) {
let { alt, src, loading = 'lazy', style, className, ...rest } = props
let [loaded, onLoad] = useImageLoadedState(src)
return (
<div
className={clsx(
'image-container overflow-hidden',
!loaded && 'animate-pulse [animation-duration:4s]',
className
)}
>
<NextImage
className={clsx(
'[transition:filter_500ms_cubic-bezier(.4,0,.2,1)]',
'h-full max-h-full w-full object-center',
loaded ? 'blur-0' : 'blur-xl'
)}
src={src}
alt={alt}
style={{ objectFit: 'cover', ...style }}
loading={loading}
priority={loading === 'eager'}
quality={100}
onLoad={onLoad}
{...rest}
/>
</div>
)
}
现在图片只会在每个页面第一次加载时显示模糊效果。
TIP
如果你想优先加载首屏可见的图片,可以将 loading
属性设置为 eager
。
我的博客就使用了这个组件来渲染图片,你可以在网站上浏览不同页面来体验这个优雅的加载效果。
Happy blurring!
On this page
← Previous post如何让你的 Namecheap 私人邮箱与 Vercel DNS 协同工作
Next post →新 macOS 系统 Web 开发环境搭建指南