算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
frontend-image-optimization【设计与创意】Use this skill when the user says 'image optimization', 'responsive images', 'image CDN', 'lazy loading images', 'next/image', 'srcset', 'picture element', 'WebP', 'AVIF', 'image compression', 'image CDN', 'blur placeholder', 'progressive image'. Optimize images for web performance including format selection, responsive images, lazy loading, and CDN …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Description: Implements image optimization — format selection, responsive images, lazy loading, CDN transforms, placeholder strategies, and performance budgets. Triggered by "image optimization", "responsive images", "image CDN", "lazy loading images", "next/image", "srcset", "picture element", "WebP", "AVIF", "image compression", "image CDN", "blur placeholder", "progressive image".
Version: 2.0.0 Author: j4flmao License: MIT
Deliver optimized images that load fast, look crisp on any screen, and minimize bandwidth usage — directly impacting LCP, CLS, and overall page weight budgets.
User request includes any of: "image optimization", "responsive images", "image CDN", "lazy loading images", "next/image", "srcset", "picture element", "WebP", "AVIF", "image compression", "image CDN", "blur placeholder", "progressive image".
Image optimization strategy with srcset/picture markup and performance notes.
## Strategy
<formats, breakpoints, CDN, placeholders>
## Markup
<srcset, picture, lazy-loading code>
## Performance
<LCP-image, preload, budgets>
—
Compression footer: frontend-image-optimization/v1 | 3 sections | formats: <avif+webp+jpeg> | LCP: <ok|warn>
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
4096 tokens
Type of image?
|-- Photograph / complex scene -->
| |-- AVIF available? --> Use AVIF (50% smaller than JPEG)
| |-- WebP available? --> Use WebP fallback
| |-- Neither --> Progressive JPEG (perceived faster load)
| NOTE: Always serve AVIF + WebP + JPEG via <picture>
|
|-- Logo / icon / illustration with few colors -->
| |-- Simple shapes? --> SVG (inline for small, sprite for many)
| |-- Has transparency? --> PNG + WebP (with transparency support)
|
|-- Screenshot / UI mockup -->
|-- AVIF with WebP fallback (good for synthetic images)
Position in viewport?
|-- Above fold (visible on load) -->
| |-- LCP candidate? -->
| | YES: Eager load + preload via `<link rel="preload">`
| | NO: Eager load (omit loading attribute)
| |
| |-- Art direction needed?
| YES: <picture> with mobile/desktop variants
| NO: <img> with srcset + sizes
|
|-- Below fold (scrolled into view) -->
|-- Native lazy loading? -->
| YES: loading="lazy" (supported in all modern browsers)
| NO: IntersectionObserver polyfill
|
|-- Placeholder needed?
YES: LQIP (blur placeholder, ~20px width)
NO: aspect-ratio container only
Perceived loading time?
|-- Instant (< 1s on fast connection) -->
| No placeholder needed, use aspect-ratio box only
|
|-- Fast (1-3s) -->
| |-- Dominant color: simplest, least data
| |-- Blur-up: better UX, needs tiny thumbnail URL
|
|-- Slow (3s+) -->
| |-- LQIP: Low Quality Image Placeholder (blurred, 20-30px)
| |-- Progressive JPEG: loads in passes, perceived faster
|
|-- Very slow / poor connectivity -->
LQIP with skeleton shimmer animation
<picture> with WebP/JPEG fallback.srcset + sizes attribute for resolution switching.1x, 2x, 3x descriptors.<picture> with media queries for mobile/crop variants.loading="lazy" attribute on <img> and <iframe>.loading="eager" or omit) for above-fold images.<link rel="preload" as="image"> in <head>.import sharp from 'sharp'
import { glob } from 'glob'
async function optimizeImages() {
const images = await glob('src/assets/images/**/*.{jpg,png}')
for (const image of images) {
const name = image.replace(/\.\w+$/, '')
// Generate AVIF
await sharp(image)
.avif({ quality: 65 })
.toFile(`${name}.avif`)
// Generate WebP
await sharp(image)
.webp({ quality: 80 })
.toFile(`${name}.webp`)
// Generate resized variants for srcset
for (const width of [640, 768, 1024, 1280]) {
await sharp(image)
.resize(width)
.jpeg({ quality: 80, progressive: true })
.toFile(`${name}-${width}w.jpg`)
}
}
}
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 768, 1024, 1280, 1536],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
remotePatterns: [
{
protocol: 'https',
hostname: 'images.ctfassets.net', // Contentful CDN
},
],
},
}
function OptimizedImage({
src,
alt,
widths = [640, 768, 1024, 1280],
sizes = '100vw',
priority = false,
}: {
src: string
alt: string
widths?: number[]
sizes?: string
priority?: boolean
}) {
const srcset = widths
.map((w) => `${src}?w=${w}&q=75 ${w}w`)
.join(', ')
return (
<picture>
<source srcSet={srcset.replace(/&q=75/g, '&fm=avif&q=65')} type="image/avif" sizes={sizes} />
<source srcSet={srcset.replace(/&q=75/g, '&fm=webp&q=75')} type="image/webp" sizes={sizes} />
<img
src={`${src}?w=${widths[0]}&q=75`}
srcSet={srcset}
sizes={sizes}
alt={alt}
loading={priority ? 'eager' : 'lazy'}
decoding="async"
style={{ aspectRatio: '16 / 9', objectFit: 'cover' }}
/>
</picture>
)
}
// Decorative image (no meaningful content)
<img src="bg.jpg" alt="" role="presentation" />
// Informative image
<img src="chart.png" alt="Bar chart showing Q4 revenue increased 25%" />
// Functional image (link, button)
<a href="/docs">
<img src="icon-pdf.svg" alt="Download PDF guide" />
</a>
// Complex image (chart, diagram)
<img
src="org-chart.png"
alt="Organization chart. CEO at top, CTO and CFO reporting to CEO."
/>
<a href="org-chart-description.html">Full organization chart description</a>
<!-- BAD -- CLS when image loads -->
<img src="photo.jpg" />
<!-- GOOD -- aspect ratio prevents layout shift -->
<div style="aspect-ratio: 16/9">
<img src="photo.jpg" />
</div>
<!-- BAD -- LCP image delayed -->
<img src="hero.jpg" loading="lazy" />
<!-- GOOD -- LCP image preloaded -->
<link rel="preload" as="image" href="hero.avif" />
<img src="hero.jpg" loading="eager" />
Serving only JPEG misses 50%+ bandwidth savings from AVIF/WebP. Always serve multiple formats via <picture>.
Every <img> must have alt text. Decorative images use alt="" (empty). Informational images need descriptive alt text.
Serving a 4000px image for a 300px thumbnail wastes bandwidth. Use srcset to serve appropriately sized variants.
| Approach | Avg File Size | Browser Support | Build Step | LCP Impact |
|---|---|---|---|---|
| JPEG (baseline) | 100% | 100% | No | Baseline |
| Progressive JPEG | ~98% | 100% | Yes | Better (perceived) |
| WebP | ~70% of JPEG | 96% | Optional | Better |
| AVIF | ~50% of JPEG | 82% | Optional | Best |
| JPEG 2000 | ~80% | Safari only | Yes | Niche |
| SVG (vector) | Variable | 100% | No | Best (tiny) |
| Factor | Image CDN | Build-Time (sharp) |
|---|---|---|
| On-the-fly transformations | Yes | No |
| Cache strategy | CDN edge cache | Static file cache |
| Deployment speed | No build step for new images | Requires rebuild |
| Cost | Per-transformation | One-time compute |
| Best for | Dynamic/user-uploaded images | Static assets, icons |
decoding="async".<img> requires alt text (empty alt="" for decorative images)longdescrole="img" + <title> when they convey meaningContent-Security-Policy: img-src to restrict image sources<picture> element — never a single format.srcset required for every content image that varies by viewport.loading="lazy" for all below-fold images; never lazy-load the LCP image.<link rel="preload" as="image"> in document <head>.<img> element for accessibility.aspect-ratio CSS or padding hack) prevents layout shift.If project requires real-time image transformation pipeline setup (Cloudinary, Imgix, or custom), deliver CDN configuration. For build-time image processing pipeline, hand off to bundler-tools skill. Otherwise implement complete image optimization.
interface ResponsiveImageProps {
src: string;
alt: string;
widths?: number[];
formats?: string[];
sizes?: string;
priority?: boolean;
className?: string;
}
function ResponsiveImage({
src,
alt,
widths = [640, 768, 1024, 1280],
formats = ['avif', 'webp'],
sizes = '100vw',
priority = false,
className,
}: ResponsiveImageProps) {
const generateSrcSet = (format: string): string => {
return widths
.map((w) => `${src}?w=${w}&fm=${format} ${w}w`)
.join(', ');
};
return (
<picture>
{formats.map((format) => (
<source
key={format}
type={`image/${format}`}
srcSet={generateSrcSet(format)}
sizes={sizes}
/>
))}
<img
src={`${src}?w=${widths[0]}`}
srcSet={generateSrcSet('jpg')}
sizes={sizes}
alt={alt}
loading={priority ? 'eager' : 'lazy'}
fetchpriority={priority ? 'high' : undefined}
decoding="async"
className={className}
/>
</picture>
);
}
interface ImageTransformOptions {
width?: number;
height?: number;
format?: 'auto' | 'webp' | 'avif' | 'jpg' | 'png';
quality?: number;
crop?: 'fill' | 'fit' | 'crop' | 'scale';
blur?: number;
}
class ImageCDN {
constructor(private baseUrl: string, private defaults: ImageTransformOptions = {}) {}
getUrl(path: string, options?: ImageTransformOptions): string {
const opts = { ...this.defaults, ...options };
const params = new URLSearchParams();
if (opts.width) params.set('w', opts.width.toString());
if (opts.height) params.set('h', opts.height.toString());
if (opts.format) params.set('fm', opts.format);
if (opts.quality) params.set('q', opts.quality.toString());
if (opts.crop) params.set('fit', opts.crop);
if (opts.blur) params.set('blur', opts.blur.toString());
return `${this.baseUrl}/${path}?${params.toString()}`;
}
}
What's the image use case?
├── Content images (blog, article, gallery)
│ └── Responsive images with srcset
│ ├── Multiple widths for different viewports
│ ├── AVIF + WebP + JPEG fallback
│ └── Lazy loading for below-fold images
│
├── Hero/LCP images (above the fold)
│ └── Preload with high priority
│ ├── <link rel="preload" as="image"> in <head>
│ ├── eager loading attribute
│ └── Smaller file size (quality trade-off)
│
├── Thumbnails / icons (small, repeated)
│ └── Inline or sprite
│ ├── SVG for icons (inline or sprite)
│ ├── Base64 inline for tiny images
│ └── CSS sprites for many small images
│
└── User-uploaded images (dynamic)
└── Image CDN with on-the-fly transformations
├── Resize, crop, format conversion on request
├── Client hints for automatic optimization
└── EXIF stripping for privacy
| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
| Serving JPEG when AVIF available | 30-50% larger file sizes | AVIF with WebP fallback in picture element |
| No srcset on images | Desktop loads mobile image (blurry) or vice versa | Always use srcset with multiple widths |
| No lazy loading on below-fold | Wastes bandwidth, slower initial load | loading="lazy" for all non-LCP images |
| Fixed image dimensions | Layout shift (CLS) on load | aspect-ratio CSS or width/height attributes |
| No preload on LCP image | Delayed largest contentful paint | <link rel="preload> in |