1f591fe2b4
- 完善产品页面布局与交互 - 优化服务详情页用户体验 - 增强新闻模块内容展示 - 改进团队页面设计 - 优化全局样式和响应式布局 - 添加分页组件支持 - 提升性能与SEO优化 - 修复已知问题与改进代码质量
113 lines
2.9 KiB
TypeScript
113 lines
2.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useCallback } from 'react';
|
|
import Image from 'next/image';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface OptimizedImageProps {
|
|
src: string;
|
|
alt: string;
|
|
width?: number;
|
|
height?: number;
|
|
fill?: boolean;
|
|
className?: string;
|
|
containerClassName?: string;
|
|
priority?: boolean;
|
|
sizes?: string;
|
|
quality?: number;
|
|
placeholder?: 'blur' | 'empty';
|
|
blurDataURL?: string;
|
|
}
|
|
|
|
export function OptimizedImage({
|
|
src,
|
|
alt,
|
|
width,
|
|
height,
|
|
fill = false,
|
|
className,
|
|
containerClassName,
|
|
priority = false,
|
|
sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
|
|
quality = 85,
|
|
placeholder = 'blur',
|
|
blurDataURL,
|
|
}: OptimizedImageProps) {
|
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
const [error, setError] = useState(false);
|
|
|
|
// 生成默认的模糊占位符
|
|
const defaultBlurDataURL = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDAiIGhlaWdodD0iNDAwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjFmNWY5Ii8+PC9zdmc+';
|
|
|
|
// 使用 callback 来处理加载状态
|
|
const handleLoad = useCallback(() => {
|
|
setIsLoaded(true);
|
|
}, []);
|
|
|
|
const handleError = useCallback(() => {
|
|
setError(true);
|
|
}, []);
|
|
|
|
if (error) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'bg-gray-100 flex items-center justify-center',
|
|
containerClassName
|
|
)}
|
|
style={!fill ? { width, height } : undefined}
|
|
>
|
|
<span className="text-gray-400 text-sm">图片加载失败</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'relative overflow-hidden',
|
|
fill ? 'w-full h-full' : '',
|
|
containerClassName
|
|
)}
|
|
style={!fill ? { width, height } : undefined}
|
|
>
|
|
{/* 模糊占位符背景 */}
|
|
{!isLoaded && placeholder === 'blur' && (
|
|
<div
|
|
className="absolute inset-0 bg-cover bg-center blur-sm scale-110 transition-opacity duration-500"
|
|
style={{
|
|
backgroundImage: `url(${blurDataURL || defaultBlurDataURL})`,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* 加载动画 */}
|
|
{!isLoaded && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-8 h-8 border-2 border-gray-200 border-t-[var(--color-brand-primary)] rounded-full animate-spin" />
|
|
</div>
|
|
)}
|
|
|
|
<Image
|
|
src={src}
|
|
alt={alt}
|
|
width={fill ? undefined : width}
|
|
height={fill ? undefined : height}
|
|
fill={fill}
|
|
priority={priority}
|
|
sizes={sizes}
|
|
quality={quality}
|
|
placeholder={placeholder}
|
|
blurDataURL={blurDataURL || defaultBlurDataURL}
|
|
className={cn(
|
|
'transition-opacity duration-500',
|
|
isLoaded ? 'opacity-100' : 'opacity-0',
|
|
className
|
|
)}
|
|
onLoad={handleLoad}
|
|
onError={handleError}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|