208 lines
4.8 KiB
TypeScript
208 lines
4.8 KiB
TypeScript
'use client';
|
|
|
|
import Image from 'next/image';
|
|
import { useState } from 'react';
|
|
|
|
interface OptimizedImageProps {
|
|
src: string;
|
|
alt: string;
|
|
width?: number;
|
|
height?: number;
|
|
className?: string;
|
|
priority?: boolean;
|
|
fill?: boolean;
|
|
sizes?: string;
|
|
quality?: number;
|
|
style?: React.CSSProperties;
|
|
objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
|
loading?: 'lazy' | 'eager';
|
|
}
|
|
|
|
/**
|
|
* OptimizedImage Component
|
|
*
|
|
* An enterprise-grade optimized image component that provides:
|
|
* - Automatic lazy loading
|
|
* - Responsive images with srcset
|
|
* - WebP/AVIF format support
|
|
* - Blur placeholder while loading
|
|
* - Error handling with fallback
|
|
* - Performance optimization
|
|
*
|
|
* @example
|
|
* <OptimizedImage
|
|
* src="/images/hero.jpg"
|
|
* alt="Hero banner showcasing our services"
|
|
* width={1200}
|
|
* height={600}
|
|
* priority={true}
|
|
* />
|
|
*/
|
|
export default function OptimizedImage({
|
|
src,
|
|
alt,
|
|
width,
|
|
height,
|
|
className = '',
|
|
priority = false,
|
|
fill = false,
|
|
sizes,
|
|
quality = 85,
|
|
style,
|
|
objectFit = 'cover',
|
|
loading,
|
|
}: OptimizedImageProps) {
|
|
const [imgSrc, setImgSrc] = useState(src);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [hasError, setHasError] = useState(false);
|
|
|
|
// Fallback image for errors
|
|
const fallbackImage = '/images/placeholder.png';
|
|
|
|
// Handle image load
|
|
const handleLoad = () => {
|
|
setIsLoading(false);
|
|
};
|
|
|
|
// Handle image error
|
|
const handleError = () => {
|
|
setHasError(true);
|
|
setIsLoading(false);
|
|
if (imgSrc !== fallbackImage) {
|
|
setImgSrc(fallbackImage);
|
|
}
|
|
};
|
|
|
|
// SEO-friendly alt text validation
|
|
const seoAlt = alt || 'GNX Soft - Enterprise Software Solutions';
|
|
|
|
// Validate alt text for SEO
|
|
if (process.env.NODE_ENV === 'development' && !alt) {
|
|
console.warn(
|
|
`OptimizedImage: Missing alt text for image "${src}". Alt text is crucial for SEO and accessibility.`
|
|
);
|
|
}
|
|
|
|
// Common image props
|
|
const imageProps = {
|
|
src: imgSrc,
|
|
alt: seoAlt,
|
|
className: `${className} ${isLoading ? 'image-loading' : 'image-loaded'}`,
|
|
onLoad: handleLoad,
|
|
onError: handleError,
|
|
quality,
|
|
loading: loading || (priority ? 'eager' : 'lazy'),
|
|
style: {
|
|
...style,
|
|
objectFit: objectFit as any,
|
|
},
|
|
};
|
|
|
|
// Use fill layout for responsive images
|
|
if (fill) {
|
|
return (
|
|
<div className={`optimized-image-wrapper ${hasError ? 'has-error' : ''}`}>
|
|
<Image
|
|
{...imageProps}
|
|
fill
|
|
sizes={sizes || '100vw'}
|
|
priority={priority}
|
|
/>
|
|
<style jsx>{`
|
|
.optimized-image-wrapper {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.optimized-image-wrapper.has-error {
|
|
background: #f3f4f6;
|
|
}
|
|
:global(.image-loading) {
|
|
filter: blur(10px);
|
|
transform: scale(1.1);
|
|
transition: filter 0.3s ease, transform 0.3s ease;
|
|
}
|
|
:global(.image-loaded) {
|
|
filter: blur(0);
|
|
transform: scale(1);
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Standard layout with explicit dimensions
|
|
return (
|
|
<div className={`optimized-image-container ${hasError ? 'has-error' : ''}`}>
|
|
<Image
|
|
{...imageProps}
|
|
width={width}
|
|
height={height}
|
|
sizes={sizes}
|
|
priority={priority}
|
|
/>
|
|
<style jsx>{`
|
|
.optimized-image-container {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
.optimized-image-container.has-error {
|
|
background: #f3f4f6;
|
|
border-radius: 4px;
|
|
}
|
|
:global(.image-loading) {
|
|
filter: blur(10px);
|
|
transform: scale(1.05);
|
|
transition: filter 0.4s ease, transform 0.4s ease;
|
|
}
|
|
:global(.image-loaded) {
|
|
filter: blur(0);
|
|
transform: scale(1);
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Usage Examples:
|
|
*
|
|
* 1. Hero Image (Priority Loading):
|
|
* <OptimizedImage
|
|
* src="/images/hero.jpg"
|
|
* alt="Enterprise software development solutions"
|
|
* width={1920}
|
|
* height={1080}
|
|
* priority={true}
|
|
* sizes="100vw"
|
|
* />
|
|
*
|
|
* 2. Service Card Image (Lazy Loading):
|
|
* <OptimizedImage
|
|
* src="/images/service/custom-software.jpg"
|
|
* alt="Custom software development service icon"
|
|
* width={400}
|
|
* height={300}
|
|
* sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
* />
|
|
*
|
|
* 3. Background Image (Fill):
|
|
* <OptimizedImage
|
|
* src="/images/background.jpg"
|
|
* alt="Technology background pattern"
|
|
* fill={true}
|
|
* sizes="100vw"
|
|
* objectFit="cover"
|
|
* />
|
|
*
|
|
* 4. Logo (High Priority):
|
|
* <OptimizedImage
|
|
* src="/images/logo.png"
|
|
* alt="GNX Soft company logo"
|
|
* width={200}
|
|
* height={50}
|
|
* priority={true}
|
|
* quality={100}
|
|
* />
|
|
*/
|
|
|