# Bildoptimierung - Dokumentation

## Übersicht

Die Media Collection nutzt Sharp für automatische Bildoptimierung. Beim Upload werden automatisch optimierte Versionen in verschiedenen Größen und Formaten erstellt.

---

## Konfiguration

### Sharp Integration

Sharp ist in der `payload.config.ts` eingebunden:

```typescript
import sharp from 'sharp'

export default buildConfig({
  // ...
  sharp,
  // ...
})
```

### Media Collection

Die Bildoptimierung ist in `src/collections/Media.ts` konfiguriert.

---

## Image Sizes

### WebP-Varianten

| Name | Breite | Höhe | Verwendung |
|------|--------|------|------------|
| `thumbnail` | 150px | 150px | Admin-Übersichten, kleine Vorschauen |
| `small` | 400px | auto | Cards, Avatare, Icons |
| `medium` | 800px | auto | Blog-Vorschauen, Testimonials |
| `large` | 1200px | auto | Hero-Sections, Vollbild |
| `xlarge` | 1920px | auto | Full-HD Displays |
| `2k` | 2560px | auto | Retina/HiDPI Displays |
| `og` | 1200x630px | fix | Social Media / Open Graph |

### AVIF-Varianten (beste Kompression)

| Name | Breite | Verwendung |
|------|--------|------------|
| `medium_avif` | 800px | Blog-Vorschauen (moderne Browser) |
| `large_avif` | 1200px | Hero-Sections (moderne Browser) |
| `xlarge_avif` | 1920px | Full-HD (moderne Browser) |

---

## Qualitätseinstellungen

| Format | Size | Qualität |
|--------|------|----------|
| WebP | thumbnail | 80% |
| WebP | small | 80% |
| WebP | medium | 82% |
| WebP | large, xlarge, 2k, og | 85% |
| AVIF | alle | 70% |

Die niedrigere AVIF-Qualität (70%) liefert bei gleicher visueller Qualität kleinere Dateien als WebP bei 85%.

---

## Fit-Modi

| Modus | Beschreibung | Verwendung |
|-------|--------------|------------|
| `cover` | Füllt die Größe, schneidet überstehende Bereiche ab | thumbnail, og |
| `inside` | Passt das Bild ein, behält Seitenverhältnis | alle anderen |

### withoutEnlargement

Alle Sizes außer `thumbnail` und `og` haben `withoutEnlargement: true`. Das verhindert, dass kleine Bilder künstlich vergrößert werden.

---

## Fokuspunkt

Die Media Collection unterstützt Fokuspunkte (`focalPoint: true`). Damit können Redakteure den wichtigsten Bereich eines Bildes markieren, der beim Cropping erhalten bleibt.

Im Admin-Panel:
1. Bild hochladen
2. Auf das Bild klicken
3. Fokuspunkt setzen

Die Fokuspunkt-Koordinaten werden in `focal_x` und `focal_y` gespeichert.

---

## Zusätzliche Felder

| Feld | Typ | Beschreibung |
|------|-----|--------------|
| `alt` | Text | Alt-Text (Pflichtfeld) |
| `caption` | Text | Bildunterschrift |
| `credit` | Text | Fotograf/Copyright |
| `tags` | Text[] | Schlagwörter für Suche |

---

## API-Response

Beim Abruf eines Media-Dokuments werden alle Sizes zurückgegeben:

```json
{
  "id": 1,
  "alt": "Beispielbild",
  "caption": "Eine Bildunterschrift",
  "credit": "Fotograf Name",
  "url": "/media/original.jpg",
  "width": 4000,
  "height": 3000,
  "mimeType": "image/jpeg",
  "filesize": 2500000,
  "focalX": 50,
  "focalY": 50,
  "sizes": {
    "thumbnail": {
      "url": "/media/original-150x150.webp",
      "width": 150,
      "height": 150,
      "mimeType": "image/webp",
      "filesize": 8500
    },
    "small": {
      "url": "/media/original-400x300.webp",
      "width": 400,
      "height": 300,
      "mimeType": "image/webp",
      "filesize": 25000
    },
    "medium": {
      "url": "/media/original-800x600.webp",
      "width": 800,
      "height": 600,
      "mimeType": "image/webp",
      "filesize": 65000
    },
    "large": {
      "url": "/media/original-1200x900.webp",
      "width": 1200,
      "height": 900,
      "mimeType": "image/webp",
      "filesize": 120000
    },
    "medium_avif": {
      "url": "/media/original-800x600.avif",
      "width": 800,
      "height": 600,
      "mimeType": "image/avif",
      "filesize": 35000
    }
    // ... weitere Sizes
  }
}
```

---

## Frontend-Integration

### Responsive Images mit srcset

```tsx
function ResponsiveImage({ media }) {
  const { url, alt, sizes } = media

  return (
    <picture>
      {/* AVIF für moderne Browser */}
      <source
        type="image/avif"
        srcSet={`
          ${sizes.medium_avif?.url} 800w,
          ${sizes.large_avif?.url} 1200w,
          ${sizes.xlarge_avif?.url} 1920w
        `}
        sizes="(max-width: 800px) 100vw, 50vw"
      />
      {/* WebP Fallback */}
      <source
        type="image/webp"
        srcSet={`
          ${sizes.small?.url} 400w,
          ${sizes.medium?.url} 800w,
          ${sizes.large?.url} 1200w,
          ${sizes.xlarge?.url} 1920w,
          ${sizes['2k']?.url} 2560w
        `}
        sizes="(max-width: 800px) 100vw, 50vw"
      />
      {/* Original als Fallback */}
      <img
        src={url}
        alt={alt}
        loading="lazy"
      />
    </picture>
  )
}
```

### Next.js Image Component

```tsx
import Image from 'next/image'

function OptimizedImage({ media }) {
  return (
    <Image
      src={media.sizes.large?.url || media.url}
      alt={media.alt}
      width={media.sizes.large?.width || media.width}
      height={media.sizes.large?.height || media.height}
      placeholder="blur"
      blurDataURL={media.sizes.thumbnail?.url}
    />
  )
}
```

### Thumbnail für Listen

```tsx
function MediaThumbnail({ media }) {
  return (
    <img
      src={media.sizes.thumbnail?.url || media.url}
      alt={media.alt}
      width={150}
      height={150}
      loading="lazy"
    />
  )
}
```

---

## Social Media / Open Graph

Die `og`-Size (1200x630px) ist optimiert für Social Media Sharing:

```tsx
// In Next.js Metadata
export async function generateMetadata({ params }) {
  const page = await getPage(params.slug)

  return {
    openGraph: {
      images: [
        {
          url: page.featuredImage?.sizes?.og?.url,
          width: 1200,
          height: 630,
        },
      ],
    },
  }
}
```

---

## Dateispeicherung

Alle Bildvarianten werden im `/media`-Verzeichnis gespeichert:

```
/media/
├── original.jpg           # Original
├── original-150x150.webp  # thumbnail
├── original-400x300.webp  # small
├── original-800x600.webp  # medium
├── original-800x600.avif  # medium_avif
├── original-1200x900.webp # large
├── original-1200x900.avif # large_avif
├── original-1920x1440.webp # xlarge
├── original-1920x1440.avif # xlarge_avif
├── original-2560x1920.webp # 2k
└── original-1200x630.webp  # og
```

---

## Kompressionsvergleich

Typische Dateigrößen für ein 4000x3000px Foto:

| Format/Size | Dateigröße | Ersparnis |
|-------------|------------|-----------|
| Original JPEG | 2.5 MB | - |
| large (WebP) | ~120 KB | 95% |
| large (AVIF) | ~70 KB | 97% |
| medium (WebP) | ~65 KB | 97% |
| medium (AVIF) | ~35 KB | 99% |
| thumbnail (WebP) | ~8 KB | 99.7% |

---

## Browser-Kompatibilität

### WebP
- Chrome 17+
- Firefox 65+
- Safari 14+
- Edge 18+

### AVIF
- Chrome 85+
- Firefox 93+
- Safari 16.4+
- Edge 121+

Für ältere Browser wird das Original-Format (JPEG/PNG) als Fallback verwendet.

---

## Datenbank-Tabellen

Die Migration `20251130_143000_media_optimization` erstellt:

- Spalten für alle Size-Varianten in `media`
- Tabelle `media_tags` für Schlagwörter

---

## Changelog

### Version 1.0 (30.11.2025)

- Sharp-Integration in payload.config.ts
- 11 Image Sizes definiert (7 WebP + 3 AVIF + OG)
- Fokuspunkt-Support aktiviert
- Zusätzliche Felder: caption, credit, tags
- Migration für Datenbank-Schema
