Vue Lightbox
Lightbox für Bilder- und Videogalerien im Vollbild für Vue 3, gebaut auf @reelkit/vue-lightbox.
Funktionen
Installation
npm install @reelkit/vue-lightbox @reelkit/vue lucide-vue-nextVergiss nicht, die Styles zu importieren:
import '@reelkit/vue-lightbox/styles.css';Icons
Die Standard-Controls nutzen lucide-vue-next für die Icons. Wenn du lieber eine andere Icon-Bibliothek verwendest, liefere mit den Scoped Slots #controls und #navigation deine eigenen.
Schnellstart
Importiere das Stylesheet und die Komponente LightboxOverlay und steuere Öffnen und Schließen mit v-model:is-open.
<script setup lang="ts">
import { ref } from 'vue';
import { LightboxOverlay, type LightboxItem } from '@reelkit/vue-lightbox';
import '@reelkit/vue-lightbox/styles.css';
const images: LightboxItem[] = [
{
src: '/cdn/samples/images/image-01.jpg',
title: 'Mountain River',
description: 'A beautiful mountain river',
},
{
src: '/cdn/samples/images/image-02.jpg',
title: 'Snowy Peaks',
},
{
src: '/cdn/samples/images/image-03.jpg',
title: 'Misty Forest',
description: 'Morning fog over the forest canopy',
},
{
src: '/cdn/samples/images/image-04.jpg',
title: 'Autumn Trail',
},
{
src: '/cdn/samples/images/image-05.jpg',
title: 'Ocean Cliff',
description: 'Dramatic cliffs above the Pacific',
},
{
src: '/cdn/samples/images/image-06.jpg',
title: 'Desert Dunes',
},
];
const open = ref(false);
const startIndex = ref(0);
function openAt(i: number) {
startIndex.value = i;
open.value = true;
}
</script>
<template>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px">
<button
v-for="(img, i) in images"
:key="img.src"
style="aspect-ratio:4/3;cursor:pointer"
@click="openAt(i)"
>
<img :src="img.src" style="width:100%;height:100%;object-fit:cover" />
</button>
</div>
<LightboxOverlay
v-model:is-open="open"
:items="images"
:initial-index="startIndex"
/>
</template>Scoped Slots
Mit sechs benannten Scoped Slots lässt sich jede Fläche des Overlays anpassen. Lass einen Slot weg, um den eingebauten Standard zu behalten; gib im Slot nichts aus (zum Beispiel mit v-if="false"), um diesen Bereich ganz auszublenden.
| Slot | Scope | Beschreibung |
|---|---|---|
| #slide | SlideSlotScope | Ersetzt den Inhalt einzelner Slides (für Video-Slides nötig) |
| #controls | ControlsSlotScope | Ersetzt die obere Leiste mit Schließen, Zähler und Vollbild |
| #navigation | NavigationSlotScope | Ersetzt die Pfeile für zurück und weiter |
| #info | InfoSlotScope | Ersetzt das untere Verlaufs-Overlay mit Titel und Beschreibung |
| #loading | LoadingSlotScope | Eigene Ladeanzeige |
| #error | ErrorSlotScope | Eigene Fehleranzeige |
<template>
<LightboxOverlay v-model:is-open="open" :items="items">
<!-- Custom info overlay -->
<template #info="{ item }">
<div class="my-caption">
<h2>{{ item.title }}</h2>
<p>{{ item.description }}</p>
</div>
</template>
<!-- Custom navigation -->
<template #navigation="{ onPrev, onNext, activeIndex, count }">
<div class="my-nav">
<button :disabled="activeIndex === 0" @click="onPrev">Prev</button>
<span>{{ activeIndex + 1 }} / {{ count }}</span>
<button :disabled="activeIndex === count - 1" @click="onNext">Next</button>
</div>
</template>
<!-- Custom controls -->
<template #controls="{ onClose, isFullscreen, onToggleFullscreen }">
<div class="my-controls">
<button @click="onToggleFullscreen">
{{ isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen' }}
</button>
<button @click="onClose">Close</button>
</div>
</template>
</LightboxOverlay>
</template>Video-Unterstützung
Video-Slides sind optional, damit das Standard-Bundle frei von Audio- und Video-Logik bleibt. Rufe useVideoSlideRenderer(items) auf und gib die zurückgegebenen VideoSlideRenderer / VideoControlsRenderer an die Slots #slide und #controls des Overlays weiter. Umschließe das Overlay mit dem zurückgegebenen SoundProvider, damit der eingebaute Ton-Schalter einen Kontext hat. Das zurückgegebene Objekt hat den Typ UseVideoSlideRendererResult: SoundProvider, hasVideo, VideoSlideRenderer und VideoControlsRenderer.
<script setup lang="ts">
import { ref } from 'vue';
import {
LightboxOverlay,
useVideoSlideRenderer,
type LightboxItem,
} from '@reelkit/vue-lightbox';
import '@reelkit/vue-lightbox/styles.css';
const open = ref(false);
const items: LightboxItem[] = [
{ src: '/image-01.jpg', title: 'Image' },
{
type: 'video',
src: '/clip.mp4',
poster: '/clip.jpg',
title: 'Clip',
},
];
const { VideoSlideRenderer, VideoControlsRenderer, SoundProvider } =
useVideoSlideRenderer(items);
</script>
<template>
<SoundProvider>
<LightboxOverlay v-model:is-open="open" :items="items">
<template #slide="scope">
<VideoSlideRenderer v-bind="scope" />
</template>
<template #controls="scope">
<VideoControlsRenderer v-bind="scope" />
</template>
</LightboxOverlay>
</SoundProvider>
</template>Das gemeinsame <video>-Element hinter den Video-Slides folgt demselben Muster wie der Reel Player für Vue — die Wiedergabe läuft unter iOS über Slide-Wechsel hinweg weiter, ohne eine Nutzergeste pro Slide zu verlangen.
Vollbild
Mit useFullscreen aus @reelkit/vue beobachtest oder schaltest du den Vollbildzustand eines referenzierten Elements. Die Lightbox steuert ihren eingebauten Vollbild-Button über dasselbe Composable.
<script setup lang="ts">
import { shallowRef } from 'vue';
import { useFullscreen } from '@reelkit/vue';
const containerRef = shallowRef<HTMLDivElement | null>(null);
const { isFullscreen, toggle } = useFullscreen({ elementRef: containerRef });
</script>
<template>
<div ref="containerRef">
<button @click="toggle">
{{ isFullscreen.value ? 'Exit fullscreen' : 'Enter fullscreen' }}
</button>
</div>
</template>URL-State
Live-Demo ansehen →Erzeuge mit useOverlayUrlState aus @reelkit/vue einen Controller und gib ihn als controller an LightboxUrlOverlay, dann gehört die Galerie der Adresszeile: Die Galerie öffnet sich, sobald der Parameter einen Slide nennt, und schließt sich, wenn der Parameter verschwindet. Links lassen sich teilen, und der Zurück-Button schließt die Galerie. Die Komponente ist von LightboxOverlay getrennt, damit jede genau einen Treiber für den Öffnungszustand hat — das Model is-open oder den URL-controller, nie beide.
Eingebaute Keys
Du kannst Slides mit einem eingebauten Key ansprechen — verteile urlIndexKey (nach Position) oder urlStableIdKey (nach einer stabilen id) in den Controller —, beide werden aus @reelkit/vue re-exportiert. Siehe den Leitfaden zum URL-State und die Core-API.
Zurück schließt nur, wenn du aus der App heraus geöffnet hast — der Link hat einen Eintrag angelegt, also führt Zurück wieder zur Galerie. Ein geteilter Link, der direkt in einem neuen Tab geöffnet wird, hat keinen Verlauf dahinter, also verlässt Zurück im Browser die Seite; der Schließen-Button oder Escape entfernt den Parameter an Ort und Stelle und lässt dich auf der Galerie.
<script setup lang="ts">
import { LightboxUrlOverlay, type LightboxItem } from '@reelkit/vue-lightbox';
import { useOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/vue';
import '@reelkit/vue-lightbox/styles.css';
const props = defineProps<{ images: LightboxItem[] }>();
const photo = useOverlayUrlState({
param: 'photo',
...urlIndexKey(() => props.images.length),
});
</script>
<template>
<!-- Opening is a link — the href is the open action. No open flag, no
handler: the overlay reads the URL and opens itself. -->
<RouterLink v-for="(img, i) in props.images" :key="img.src" :to="`?photo=${i}`">
<img :src="img.src" />
</RouterLink>
<LightboxUrlOverlay :controller="photo" :items="props.images" />
</template>Das Composable nimmt ein Options-Objekt und gibt einen UrlStateController zurück (mit set, index, value). Behalte ihn für die Steuerung aus dem Code: set ist der Schreibzugriff auf unterster Ebene, den das Overlay intern nutzt (Slide-Wechsel und set(null) zum Schließen). Damit steuerst du das Overlay auch selbst — set(index) öffnet es, genau wie die Navigation zum Parameter. Zum Öffnen ist ein Link trotzdem besser: Der href lässt sich teilen, öffnet sich in einem neuen Tab, und der Zurück-Button schließt ihn — alles ohne Handler.
Alle Optionen von useOverlayUrlState (param, adapter, codec, locator): siehe die API-Referenz für Vue.
LightboxUrlOverlay selbst nimmt nur :controller (Pflicht), ein Emit @close sowie jedes Prop für Darstellung und Verhalten, das LightboxOverlay weitergibt (items, transition-fn, die Scoped Slots und so weiter) — aber kein is-open.
- Das Öffnen kostet einen Verlaufseintrag; das Blättern ersetzt ihn, also fügen hundert Wischgesten keinen hinzu — ein Schritt zurück verlässt die Galerie immer.
- Ein geteilter Link wie
?photo=3öffnet die Galerie bei diesem Slide. Ein Parameter, der keinen Slide nennt, wird aus der URL entfernt, statt einen Slide zu behaupten, der sich nicht öffnen lässt.
In einer App mit Router übergib einen Adapter. Wer direkt in den Verlauf schreibt, lässt die Location des Routers veralten, und seine nächste Navigation verwirft den Parameter.
<script setup lang="ts">
import { useVueRouterUrlAdapter } from '@reelkit/vue/vue-router-url-adapter';
const adapter = useVueRouterUrlAdapter();
const photo = useOverlayUrlState({
param: 'photo',
adapter,
...urlIndexKey(() => images.length),
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="images" />
</template>Stabile Links. Der Index ist positionsbasiert, also öffnet ein Lesezeichen ein anderes Bild, sobald die Liste neu sortiert wird. urlStableIdKey verwendet die stabile id jedes Elements und durchsucht die aktuelle Liste — ein Aufruf deckt den üblichen Fall ab.
<script setup lang="ts">
const photo = useOverlayUrlState({
param: 'photo',
...urlStableIdKey({ items: () => images }),
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="images" />
</template>Übergib hashCodec: base64UrlCodec, um die id in der URL als base64url zu kodieren — eine umkehrbare Verschleierung, kein kryptografischer Hash.
Willst du nach einem anderen Feld (einem slug) adressieren oder einen endlosen Feed mit locateAsync nachladen, baust du codec/locator selbst: codec schreibt die Identität in die URL, locator findet, wo sie jetzt steht.
<script setup lang="ts">
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => images.findIndex((x) => x.slug === id),
identify: (index) => images[index].slug,
},
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="images" />
</template>Endlose / paginierte Galerien. locate ist synchron und kann daher nur für bereits geladene Elemente antworten — ein geteilter Link auf Bild 400 eines Feeds, der 20 geladen hat, findet nichts. locateAsync ist der Rückfall, der nur bei einem Fehlschlag aufgerufen wird: Lade die Seiten, die du brauchst, und gib dann den Index zurück, den die Identität tatsächlich hat.
Abkürzung
Adressierst du über die id des Elements? Dann spar dir Codec und Locator von Hand — übergib locateAsync direkt an urlStableIdKey({ items, locateAsync }) (es lädt bei einem Fehlschlag nach und gibt dann den Index zurück). Die ausführlichere Version unten ist für ein anderes Feld oder für volle Kontrolle.
<script setup lang="ts">
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => items.value.findIndex((x) => x.id === id),
identify: (index) => items.value[index].id,
locateAsync: async (id) => {
const loaded = await loadById(id); // or loadUntil(id) — fetch just that one, or page up to it
if (!loaded) return null; // exhausted — link names no item
items.value = loaded; // commit; the overlay renders from this
return loaded.findIndex((x) => x.id === id); // wherever it landed
},
},
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="items" />
</template>Solange die Antwort aussteht, bleibt die Lightbox geschlossen und der Parameter unangetastet, damit der Deep Link das Laden übersteht. null oder eine Ablehnung entfernt den Parameter. Eine Antwort, die eintrifft, nachdem die URL weitergewandert ist, nach dem Schließen oder nach dem Unmount, wird verworfen, damit ein langsamer Abruf keinen Slide öffnet, den niemand angefragt hat. Was sie zurückgibt, ist maßgeblich — sie meldet den Index von Daten, die sie gerade geladen hat, und die Lightbox übernimmt ihn unverändert, statt items erneut zu lesen, das Vue noch nicht neu gerendert hat.
Automatisches Weiterschalten
Setze auto-advance, damit die Galerie beim Öffnen als Diashow läuft. Standardmäßig aus, also rendert und verhält sich eine bestehende Lightbox wie bisher.
<script setup lang="ts">
import { ref } from 'vue';
import { LightboxOverlay, type LightboxApi } from '@reelkit/vue-lightbox';
const open = ref(false);
const lightbox = ref<LightboxApi | null>(null);
</script>
<template>
<LightboxOverlay
ref="lightbox"
v-model:is-open="open"
:items="items"
auto-advance
:auto-advance-interval="4000"
/>
<!-- The template ref drives it from outside: lightbox?.pause() -->
</template>- Bilder bleiben für
auto-advance-intervalstehen (standardmäßig 5000 ms), gezählt ab dem Moment, in dem das Bild geladen ist, damit ein langsames Bild nie übersprungen wird, bevor es jemand gesehen hat. Ein Bild, das nicht lädt, zählt ab seinem Fehler. - Videos laufen einmal statt in Schleife und schalten weiter, wenn sie enden.
VideoSlideRenderererledigt das für dich; ein eigener#slideliestautoAdvanceaus seinem Scope und meldet das Ende überonEnded. - Sie wartet, solange die Maus auf einem Control ruht (einem Button, einem Link oder der Bildunterschrift), während einer Wischgeste und solange die Seite verborgen ist, und läuft dann dort weiter, wo sie angehalten hat.
- Wechselst du von Hand (Pfeile, Tasten, Mausrad, Wischen), beginnt das Intervall des neuen Slides bei null; die Diashow bleibt an.
- Auf dem letzten Slide hält sie an, außer
loopist gesetzt. Der Play/Pause-Button in den Controls zeigt den Zustand und lässt die Betrachtenden ihn umschalten;play()undpause()an der LightboxApi tun dasselbe aus dem Code.
Ein eigener Slot #controls erhält isAutoAdvancing und, nur wenn auto-advance an ist, onToggleAutoAdvance — rendere einen PlayPauseButton, wenn es vorhanden ist. Das Timing selbst ist createSlideshowController aus @reelkit/core; siehe den Leitfaden zu Autoplay und die Core-API.
Download
Setze show-download, um den Controls einen Download-Button hinzuzufügen. Er speichert das aktive Element aus seiner downloadUrl, ersatzweise aus src, unter seinem downloadName, ersatzweise unter dem letzten Segment der URL.
<script setup lang="ts">
import { ref } from 'vue';
import {
CloseButton,
DownloadButton,
LightboxOverlay,
type LightboxItem,
} from '@reelkit/vue-lightbox';
const open = ref(false);
const items: LightboxItem[] = [
{
src: 'https://cdn.example.com/beach-1600.jpg',
// Save the original rather than the resized preview
downloadUrl: 'https://cdn.example.com/beach-original.jpg',
downloadName: 'beach.jpg',
},
];
</script>
<template>
<LightboxOverlay v-model:is-open="open" :items="items" show-download>
<!-- A custom bar shows the buttons only when their handler is there -->
<template #controls="{ onClose, isDownloading, onDownload }">
<DownloadButton
v-if="onDownload"
:is-downloading="isDownloading"
:on-press="onDownload"
/>
<CloseButton :on-press="onClose" />
</template>
</LightboxOverlay>
</template>Die Datei wird abgerufen und gespeichert, weil ein Browser das Attribut download an einem Link auf einen anderen Origin ignoriert und die Datei stattdessen öffnen würde. Schlägt der Abruf fehl — meist ein Server auf einem anderen Origin, der keine CORS-Header sendet —, öffnet sich die Datei in einem neuen Tab, wo man sie trotzdem speichern kann. Liefere die Dateien mit Access-Control-Allow-Origin aus, damit ein Klick zum Speichern genügt. Während ein Download läuft, ist der Button deaktiviert.
Ein eigener Slot #controls erhält isDownloading und, nur wenn show-download an ist, onDownload — rendere einen DownloadButton, wenn es vorhanden ist. Der Helfer ist downloadFile aus @reelkit/core; siehe den Leitfaden zum Download.
Control-Buttons
| Komponente | Props | Beschreibung |
|---|---|---|
PlayPauseButton | PlayPauseButtonProps: isPlaying, onPress, className? | Schalter für die Diashow (Icon Play/Pause, aria-pressed während der Wiedergabe). Wird von den Standard-Controls mit auto-advance angezeigt. |
DownloadButton | DownloadButtonProps: isDownloading, onPress, className? | Download-Button, deaktiviert solange isDownloading. Wird von den Standard-Controls mit show-download angezeigt. |
CloseButton | CloseButtonProps: onPress, className? | Standard-Schließen-Button, den die Leiste der Controls rendert; setze ihn in deinen eigenen Slot #controls, um das eingebaute Aussehen zu behalten. |
Counter | CounterProps: currentIndex, count, className? | Standardzähler "3 / 10" als Chip; setze ihn in deinen eigenen Slot #controls, um das eingebaute Aussehen zu behalten. |
FullscreenButton | FullscreenButtonProps: isFullscreen, onPress, className? | Standard-Schalter für das Vollbild; sein Icon folgt isFullscreen. |
SoundButton | SoundButtonProps: isMuted, onPress, className? | Standard-Schalter für den Ton bei Video-Elementen; sein Icon folgt isMuted. |
Teile für Slides und Navigation
| Komponente | Props | Beschreibung |
|---|---|---|
ImageSlide | ImageSlideProps: src, alt?, isActive, onReady?, onLoadError? | Standard-Bild-Slide, den das Overlay rendert; setze ihn in deinen eigenen Slot #slide, um für Bild-Elemente das eingebaute Aussehen zu behalten. |
LightboxVideoSlide | LightboxVideoSlideProps: src, poster?, isActive, size, slideKey, loop?, onPlaying?, onWaiting?, onLoadError? | Standard-Video-Slide, der über das gemeinsame <video>-Element abspielt; useVideoSlideRenderer rendert ihn für dich. |
LightboxNavigation | LightboxNavigationProps: activeIndex, count, loop?, onPrev, onNext | Standardpfeile für zurück und weiter; setze sie in deinen eigenen Slot #navigation, um das eingebaute Aussehen zu behalten. |
API-Referenz
Props von LightboxOverlay
LightboxOverlayProps
| Prop | Typ | Standard | Beschreibung |
|---|---|---|---|
isOpen | boolean | Pflicht | Steuert die Sichtbarkeit; bei false wird das Overlay aus dem DOM entfernt. Bindbar über v-model:is-open. |
items | LightboxItem[] | Pflicht | Array der Elemente (Bilder oder Videos) |
initialIndex | number | 0 | Nullbasierter Index des zuerst sichtbaren Elements |
transitionFn | TransitionTransformFn | slideTransition | Funktion für den Slide-Übergang. Importiere einen eingebauten (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) oder übergib einen eigenen. Ohne Angabe gilt slideTransition. |
showInfo | boolean | true | Ob das Info-Overlay mit Titel und Beschreibung gerendert wird |
showControls | boolean | true | Ob die obere Leiste mit Schließen, Zähler und Vollbild gerendert wird |
showNavigation | boolean | true | Ob die Pfeile für zurück und weiter gerendert werden (nur Desktop) |
transitionDuration | number | 300 | Dauer der Slide-Animation in ms |
swipeDistanceFactor | number | 0.12 | Mindestanteil der Wischstrecke (0–1), der einen Slide-Wechsel auslöst |
swipeToCloseDirection | 'up' | 'down' | 'up' | Richtung der Wischgeste zum Schließen auf Mobilgeräten |
loop | boolean | false | Ob der Slider vom letzten Slide zurück zum ersten springt |
enableNavKeys | boolean | true | Aktiviert die Navigation mit den Pfeiltasten |
enableWheel | boolean | true | Aktiviert die Navigation mit dem Mausrad |
wheelDebounceMs | number | 200 | Debounce-Dauer für Mausrad-Events in ms |
autoAdvance | boolean | false | Spielt die Galerie als Diashow ab. Siehe Automatisches Weiterschalten. |
autoAdvanceInterval | number | 5000 | Wie lange jedes Bild während einer Diashow stehen bleibt, in ms, gezählt ab dem Laden |
showDownload | boolean | false | Fügt einen Download-Button für das aktive Element hinzu. Siehe Download. |
ariaLabel | string | 'Image gallery' | Zugängliches Label für den Dialogbereich |
Props von LightboxUrlOverlay
LightboxUrlOverlayProps
Nimmt jedes Prop für Darstellung und Verhalten von oben außer is-open und ersetzt es durch einen controller. Es sendet close, slide-change und api-ready, aber kein update:is-open. initial-index wird hier ignoriert — die Position des Controllers wählt den Slide, also würde ein daneben übergebener Wert bei jedem Öffnen überschrieben.
| Prop | Typ | Standard | Beschreibung |
|---|---|---|---|
controller | UrlStateController | Pflicht | Controller aus useOverlayUrlState. Seine Position entscheidet, ob das Overlay offen ist und welchen Slide es zeigt; das Overlay schreibt beim Slide-Wechsel und beim Schließen über ihn zurück. |
Events von LightboxOverlay
| Event | Payload | Beschreibung |
|---|---|---|
close | void | Wird gesendet, wenn die Lightbox geschlossen wird |
slide-change | number | Wird nach einem Wechsel mit dem neuen aktiven Slide-Index gesendet |
api-ready | LightboxApi | Wird einmal gesendet, sobald der Slider bereit ist, und stellt die imperative API bereit |
update:is-open | boolean | Wird beim Schließen gesendet; ermöglicht v-model:is-open |
LightboxApi
Das Objekt, das api-ready sendet, auch über die Template-Ref der Komponente verfügbar. Der Slider existiert nur, solange das Overlay offen ist, also tut jede Slider-Methode im geschlossenen Zustand nichts; close() funktioniert immer.
| Methode | Typ | Beschreibung |
|---|---|---|
next() | () => void | Wechselt animiert zum nächsten Slide. Bleibt auf dem letzten Slide, außer loop ist an. |
prev() | () => void | Wechselt animiert zum vorherigen Slide. Bleibt auf dem ersten Slide, außer loop ist an. |
goTo(index, animate?) | (number, boolean?) => Promise<void> | Springt zu einem Slide. index wird auf die Elemente begrenzt, animate ist standardmäßig false. Löst auf, wenn der Wechsel fertig ist. |
adjust() | () => void | Berechnet die Slide-Position nach einer Layout-Änderung neu, die das Overlay selbst nicht bemerkt hat |
observe() | () => void | Hört nach unobserve() wieder auf Gesten, Tastatur und Mausrad |
unobserve() | () => void | Hört nicht mehr auf Gesten, Tastatur und Mausrad; die Slides bleiben, wo sie sind |
close() | () => void | Schließt die Lightbox, genau wie der Schließen-Button oder Escape |
play() | () => void | Startet die Diashow und setzt den aktiven Slide dort fort, wo er angehalten hat. Ohne auto-advance wirkungslos. |
pause() | () => void | Hält die Diashow an; der aktive Slide behält seinen Fortschritt. Ohne auto-advance wirkungslos. |
Interface LightboxItem
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
src | string | ja | URL des Bildes oder Videos |
type | 'image' | 'video' | nein | Art des Elements. Standard ist 'image' |
poster | string | nein | Vorschaubild für Videos |
title | string | nein | Titel im Info-Overlay |
description | string | nein | Beschreibung unter dem Titel |
width | number | nein | Eigene Bildbreite in Pixeln |
height | number | nein | Eigene Bildhöhe in Pixeln |
downloadUrl | string | nein | Was der Download-Button speichert. Standard ist src |
downloadName | string | nein | Dateiname beim Speichern. Standard ist das letzte Segment des URL-Pfads |
Typen der Slot-Scopes
| Typ | Felder |
|---|---|
SlideSlotScope | { item, index, size: [number, number], isActive, autoAdvance, onReady, onWaiting, onError, onEnded } |
ControlsSlotScope | { item, activeIndex, count, isFullscreen, isAutoAdvancing, isDownloading, onClose, onToggleFullscreen, onToggleAutoAdvance?, onDownload? } |
NavigationSlotScope | { item, activeIndex, count, onPrev, onNext } |
InfoSlotScope | { item, index } |
LoadingSlotScope | { item, activeIndex } |
ErrorSlotScope | { item, activeIndex } |
Übergänge
Übergib eine beliebige TransitionTransformFn über das Prop transition-fn. Importierst du nur den Übergang, den du nutzt, kann der Bundler den Rest per Tree-Shaking entfernen. Ohne Angabe gilt slideTransition.
| Funktion | Beschreibung |
|---|---|
slideTransition | Standard. Horizontale Verschiebung zwischen den Slides; re-exportiert aus @reelkit/vue. |
lightboxFadeTransition | Überblendung mit einem leichten horizontalen Versatz. Nur in @reelkit/vue-lightbox. |
flipTransition | 3D-Drehung um die Y-Achse; re-exportiert aus @reelkit/vue. |
lightboxZoomTransition | Der neue Slide wächst mit Überblendung von 70% auf 100%. Nur in @reelkit/vue-lightbox. |
<script setup lang="ts">
import {
LightboxOverlay,
lightboxFadeTransition,
lightboxZoomTransition,
} from '@reelkit/vue-lightbox';
</script>
<template>
<!-- Built-in transition -->
<LightboxOverlay
v-model:is-open="open"
:items="items"
:transition-fn="lightboxFadeTransition"
/>
<!-- Different built-in -->
<LightboxOverlay
v-model:is-open="open"
:items="items"
:transition-fn="lightboxZoomTransition"
/>
</template>Laden von Inhalten und Fehlerbehandlung
Wenn du das Rendern über den Slot #slide übernimmst, stehen im Slot-Scope drei Lifecycle-Callbacks bereit, um den Ladezustand zu melden. Die Lightbox verfolgt den Zustand jedes Slides und zeigt entsprechend einen Spinner oder ein Fehlersymbol. Ein Preloader merkt sich defekte URLs, damit ein erneuter Besuch eines fehlgeschlagenen Slides keinen neuen Versuch startet.
Lifecycle-Callbacks
| Callback | Typ | Beschreibung |
|---|---|---|
onReady | () => void | Meldet, dass der Inhalt des Slides erfolgreich geladen ist (zum Beispiel das Bild dekodiert) |
onWaiting | () => void | Meldet, dass der Inhalt des Slides lädt oder puffert (zeigt den Spinner) |
onError | () => void | Meldet, dass der Inhalt des Slides nicht geladen werden konnte (zeigt das Fehlersymbol) |
Callbacks in #slide verdrahten
<template>
<LightboxOverlay v-model:is-open="open" :items="items">
<template
#slide="{ item, size, isActive, onReady, onWaiting, onError }"
>
<template v-if="item.type === 'video'">
<video
:src="item.src"
:poster="item.poster"
:autoplay="isActive"
:style="{ width: `${size[0]}px`, height: `${size[1]}px`, objectFit: 'contain' }"
@canplay="onReady"
@waiting="onWaiting"
@error="onError"
/>
</template>
<template v-else>
<img
:src="item.src"
:style="{ width: `${size[0]}px`, height: `${size[1]}px`, objectFit: 'contain' }"
@load="onReady"
@error="onError"
/>
</template>
</template>
</LightboxOverlay>
</template>Eigener Lade-Slot
Mit dem Slot #loading ersetzt du den Standard-Spinner.
<template>
<LightboxOverlay v-model:is-open="open" :items="items">
<template #loading="{ item, activeIndex }">
<div class="my-loading">
<span>Loading image {{ activeIndex + 1 }}…</span>
<span class="muted">{{ item.title }}</span>
</div>
</template>
</LightboxOverlay>
</template>Eigener Fehler-Slot
Mit dem Slot #error ersetzt du das Standard-Symbol für ein defektes Bild.
<template>
<LightboxOverlay v-model:is-open="open" :items="items">
<template #error="{ item, activeIndex }">
<div class="my-error">
<span>Failed to load</span>
<span class="muted">{{ item.title ?? item.src }}</span>
</div>
</template>
</LightboxOverlay>
</template>CSS-Klassen
Alle CSS-Klassen sind normal (nicht scoped), also lassen sie sich mit spezifischeren Selektoren in einem Stylesheet ansprechen, das nach @reelkit/vue-lightbox/styles.css geladen wird. Für Farben, Größen und z-index nimm lieber die CSS Custom Properties aus dem Abschnitt Theming weiter unten.
| Klasse | Komponente | Beschreibung |
|---|---|---|
.rk-lightbox-overlay | Overlay | Wurzel-Container (Hintergrund im Vollbild) |
.rk-lightbox-top-shade | Overlay | Oberer Verlauf hinter den Controls |
.rk-lightbox-spinner | Overlay | Standard-Ladespinner |
.rk-lightbox-error | Overlay | Container für den Fehlerzustand (defektes Bild) |
.rk-lightbox-error-text | Overlay | Textlabel des Fehlerzustands |
.rk-lightbox-controls-left | Controls | Container der Controls oben links |
.rk-lightbox-btn | Controls | Control-Button (Vollbild, Ton und so weiter) |
.rk-lightbox-play-pause | Controls | Play/Pause-Button der Diashow (mit .rk-lightbox-btn) |
.rk-lightbox-download | Controls | Download-Button (mit .rk-lightbox-btn) |
.rk-lightbox-close | Controls | Schließen-Button |
.rk-lightbox-counter | Controls | Chip mit dem Bildzähler |
.rk-lightbox-nav | Navigation | Navigationspfeil (zurück und weiter) |
.rk-lightbox-nav-prev | Navigation | Pfeil zurück |
.rk-lightbox-nav-next | Navigation | Pfeil weiter |
.rk-lightbox-info | Info | Container für Titel und Beschreibung |
.rk-lightbox-info-title | Info | Bildtitel |
.rk-lightbox-info-description | Info | Bildbeschreibung |
.rk-lightbox-slide | Slide | Slide-Container |
.rk-lightbox-img | Slide | Bildelement |
.rk-lightbox-video-container | VideoSlide | Container eines Video-Slides (optional) |
.rk-lightbox-video-element | VideoSlide | Videoelement (optional) |
.rk-lightbox-video-poster | VideoSlide | Posterbild des Videos (optional) |
Theming
Überschreibe eine beliebige CSS Custom Property --rk-lightbox-* auf :root (oder einem Vorfahren von .rk-lightbox-overlay), um das Aussehen anzupassen. Direkte Deklarationen auf .rk-lightbox-overlay würden geerbte Werte überdecken, also setze Überschreibungen auf einem Vorfahren-Selektor.
| Token | Standard | Steuert |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | Farbe des Hintergrunds |
--rk-lightbox-overlay-z | 9999 | z-index des Overlays |
--rk-lightbox-top-shade-height | 80px | Höhe des oberen Verlaufs |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | Verlauf oben |
--rk-lightbox-edge-padding | 16px | Randabstand für Schließen, Navigation und Controls |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | Standard-Hintergrund für Schließen, Navigation und kleine Buttons |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | Hover-Hintergrund für Schließen, Navigation und kleine Buttons |
--rk-lightbox-btn-fg | #fff | Icon-Farbe für Schließen, Navigation und kleine Buttons |
--rk-lightbox-btn-size | 36px | Größe der kleinen Buttons (Vollbild-Schalter und so weiter) |
--rk-lightbox-btn-disabled-opacity | 0.5 | Deckkraft eines deaktivierten kleinen Buttons (Download läuft) |
--rk-lightbox-close-size | 40px | Größe des Schließen-Buttons |
--rk-lightbox-nav-size | 48px | Größe der Pfeile zurück und weiter |
--rk-lightbox-nav-opacity | 0.7 | Deckkraft der Pfeile im Ruhezustand |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | Hintergrund des Zähler-Chips |
--rk-lightbox-counter-fg | #fff | Textfarbe des Zählers |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | Verlauf hinter der Bildunterschrift |
--rk-lightbox-title-size | 18px | Schriftgröße des Titels |
--rk-lightbox-description-size | 14px | Schriftgröße der Beschreibung |
--rk-lightbox-video-bg | #000 | Hintergrund der Letterbox hinter <video> |
<style>
:root {
--rk-lightbox-overlay-bg: #0f172a;
--rk-lightbox-btn-bg: rgba(99, 102, 241, 0.65);
--rk-lightbox-btn-bg-hover: rgba(168, 85, 247, 0.85);
--rk-lightbox-info-bg: linear-gradient(
transparent,
rgba(99, 102, 241, 0.55) 60%,
rgba(168, 85, 247, 0.85)
);
}
</style>Barrierefreiheit
Die Wurzel des Overlays ist ein modaler Dialog (role="dialog", aria-modal="true"). Mit dem Prop aria-label änderst du die Ansage für Screenreader; Standard ist "Image gallery". Jeder Slide trägt role="group", aria-roledescription="slide" und ein aus der Position abgeleitetes aria-label (zum Beispiel "Image 2 of 5").
Die Lightbox übernimmt beim Öffnen den Fokus und gibt ihn beim Schließen an den Auslöser zurück. Tab und Shift+Tab wechseln zwischen den fokussierbaren Elementen darin; Fokus, der entwischt (Klick außerhalb, Fokus aus dem Code), wird zurückgeholt. Umgesetzt mit captureFocusForReturn und createFocusTrap aus @reelkit/vue.
Tastenkürzel
| Taste | Aktion |
|---|---|
ArrowLeft | Vorheriges Bild |
ArrowRight | Nächstes Bild |
Escape | Lightbox schließen (oder Vollbild verlassen, falls aktiv) |