Vue Lightbox

Lightbox für Bilder- und Videogalerien im Vollbild für Vue 3, gebaut auf @reelkit/vue-lightbox.

Live-Demo ansehen →

Funktionen

Bilder und Video
Video-Slides eingebaut
Touch-Gesten
Wischen zum Navigieren
Wischen zum Schließen
Nach oben wischen zum Schließen
Tastatur
Pfeiltasten + Escape
Vollbild
API für alle Browser
Übergänge
Slide, Fade, Flip, Zoom-in
Vorladen
±2 Nachbarn vorab geladen
Ton an/aus
Stummschalten pro Slide
Ladezustände
Spinner + eigener Slot
Fehlerbehandlung
Fehlersymbol + eigener Slot
Scoped Slots
6 anpassbare Slot-Bereiche
v-model
Zwei-Wege-Bindung mit v-model:is-open
URL-State
Teilbare Links mit Lesezeichen
Diashow
Optionales Weiterschalten mit Play/Pause
Download
Optionaler Button, der das aktive Element speichert

Installation

bash
npm install @reelkit/vue-lightbox @reelkit/vue lucide-vue-next

Vergiss nicht, die Styles zu importieren:

typescript
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.

App.vue
<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.

SlotScopeBeschreibung
#slideSlideSlotScopeErsetzt den Inhalt einzelner Slides (für Video-Slides nötig)
#controlsControlsSlotScopeErsetzt die obere Leiste mit Schließen, Zähler und Vollbild
#navigationNavigationSlotScopeErsetzt die Pfeile für zurück und weiter
#infoInfoSlotScopeErsetzt das untere Verlaufs-Overlay mit Titel und Beschreibung
#loadingLoadingSlotScopeEigene Ladeanzeige
#errorErrorSlotScopeEigene Fehleranzeige
vue
<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.

vue
<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.

vue
<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.

vue
<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.

vue
<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.

vue
<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.

vue
<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.

vue
<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.

vue
<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-interval stehen (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. VideoSlideRenderer erledigt das für dich; ein eigener #slide liest autoAdvance aus seinem Scope und meldet das Ende über onEnded.
  • 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 loop ist gesetzt. Der Play/Pause-Button in den Controls zeigt den Zustand und lässt die Betrachtenden ihn umschalten; play() und pause() 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.

vue
<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

KomponentePropsBeschreibung
PlayPauseButtonPlayPauseButtonProps: 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.
DownloadButtonDownloadButtonProps: isDownloading, onPress, className?Download-Button, deaktiviert solange isDownloading. Wird von den Standard-Controls mit show-download angezeigt.
CloseButtonCloseButtonProps: 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.
CounterCounterProps: currentIndex, count, className?Standardzähler "3 / 10" als Chip; setze ihn in deinen eigenen Slot #controls, um das eingebaute Aussehen zu behalten.
FullscreenButtonFullscreenButtonProps: isFullscreen, onPress, className?Standard-Schalter für das Vollbild; sein Icon folgt isFullscreen.
SoundButtonSoundButtonProps: isMuted, onPress, className?Standard-Schalter für den Ton bei Video-Elementen; sein Icon folgt isMuted.

Teile für Slides und Navigation

KomponentePropsBeschreibung
ImageSlideImageSlideProps: 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.
LightboxVideoSlideLightboxVideoSlideProps: 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.
LightboxNavigationLightboxNavigationProps: activeIndex, count, loop?, onPrev, onNextStandardpfeile 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

PropTypStandardBeschreibung
isOpenbooleanPflichtSteuert die Sichtbarkeit; bei false wird das Overlay aus dem DOM entfernt. Bindbar über v-model:is-open.
itemsLightboxItem[]PflichtArray der Elemente (Bilder oder Videos)
initialIndexnumber0Nullbasierter Index des zuerst sichtbaren Elements
transitionFnTransitionTransformFnslideTransitionFunktion für den Slide-Übergang. Importiere einen eingebauten (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) oder übergib einen eigenen. Ohne Angabe gilt slideTransition.
showInfobooleantrueOb das Info-Overlay mit Titel und Beschreibung gerendert wird
showControlsbooleantrueOb die obere Leiste mit Schließen, Zähler und Vollbild gerendert wird
showNavigationbooleantrueOb die Pfeile für zurück und weiter gerendert werden (nur Desktop)
transitionDurationnumber300Dauer der Slide-Animation in ms
swipeDistanceFactornumber0.12Mindestanteil der Wischstrecke (0–1), der einen Slide-Wechsel auslöst
swipeToCloseDirection'up' | 'down''up'Richtung der Wischgeste zum Schließen auf Mobilgeräten
loopbooleanfalseOb der Slider vom letzten Slide zurück zum ersten springt
enableNavKeysbooleantrueAktiviert die Navigation mit den Pfeiltasten
enableWheelbooleantrueAktiviert die Navigation mit dem Mausrad
wheelDebounceMsnumber200Debounce-Dauer für Mausrad-Events in ms
autoAdvancebooleanfalseSpielt die Galerie als Diashow ab. Siehe Automatisches Weiterschalten.
autoAdvanceIntervalnumber5000Wie lange jedes Bild während einer Diashow stehen bleibt, in ms, gezählt ab dem Laden
showDownloadbooleanfalseFügt einen Download-Button für das aktive Element hinzu. Siehe Download.
ariaLabelstring'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.

PropTypStandardBeschreibung
controllerUrlStateControllerPflichtController 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

EventPayloadBeschreibung
closevoidWird gesendet, wenn die Lightbox geschlossen wird
slide-changenumberWird nach einem Wechsel mit dem neuen aktiven Slide-Index gesendet
api-readyLightboxApiWird einmal gesendet, sobald der Slider bereit ist, und stellt die imperative API bereit
update:is-openbooleanWird 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.

MethodeTypBeschreibung
next()() => voidWechselt animiert zum nächsten Slide. Bleibt auf dem letzten Slide, außer loop ist an.
prev()() => voidWechselt 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()() => voidBerechnet die Slide-Position nach einer Layout-Änderung neu, die das Overlay selbst nicht bemerkt hat
observe()() => voidHört nach unobserve() wieder auf Gesten, Tastatur und Mausrad
unobserve()() => voidHört nicht mehr auf Gesten, Tastatur und Mausrad; die Slides bleiben, wo sie sind
close()() => voidSchließt die Lightbox, genau wie der Schließen-Button oder Escape
play()() => voidStartet die Diashow und setzt den aktiven Slide dort fort, wo er angehalten hat. Ohne auto-advance wirkungslos.
pause()() => voidHält die Diashow an; der aktive Slide behält seinen Fortschritt. Ohne auto-advance wirkungslos.

Interface LightboxItem

FeldTypPflichtBeschreibung
srcstringjaURL des Bildes oder Videos
type'image' | 'video'neinArt des Elements. Standard ist 'image'
posterstringneinVorschaubild für Videos
titlestringneinTitel im Info-Overlay
descriptionstringneinBeschreibung unter dem Titel
widthnumberneinEigene Bildbreite in Pixeln
heightnumberneinEigene Bildhöhe in Pixeln
downloadUrlstringneinWas der Download-Button speichert. Standard ist src
downloadNamestringneinDateiname beim Speichern. Standard ist das letzte Segment des URL-Pfads

Typen der Slot-Scopes

TypFelder
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.

FunktionBeschreibung
slideTransitionStandard. Horizontale Verschiebung zwischen den Slides; re-exportiert aus @reelkit/vue.
lightboxFadeTransitionÜberblendung mit einem leichten horizontalen Versatz. Nur in @reelkit/vue-lightbox.
flipTransition3D-Drehung um die Y-Achse; re-exportiert aus @reelkit/vue.
lightboxZoomTransitionDer neue Slide wächst mit Überblendung von 70% auf 100%. Nur in @reelkit/vue-lightbox.
vue
<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

CallbackTypBeschreibung
onReady() => voidMeldet, dass der Inhalt des Slides erfolgreich geladen ist (zum Beispiel das Bild dekodiert)
onWaiting() => voidMeldet, dass der Inhalt des Slides lädt oder puffert (zeigt den Spinner)
onError() => voidMeldet, dass der Inhalt des Slides nicht geladen werden konnte (zeigt das Fehlersymbol)

Callbacks in #slide verdrahten

vue
<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.

vue
<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.

vue
<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.

KlasseKomponenteBeschreibung
.rk-lightbox-overlayOverlayWurzel-Container (Hintergrund im Vollbild)
.rk-lightbox-top-shadeOverlayOberer Verlauf hinter den Controls
.rk-lightbox-spinnerOverlayStandard-Ladespinner
.rk-lightbox-errorOverlayContainer für den Fehlerzustand (defektes Bild)
.rk-lightbox-error-textOverlayTextlabel des Fehlerzustands
.rk-lightbox-controls-leftControlsContainer der Controls oben links
.rk-lightbox-btnControlsControl-Button (Vollbild, Ton und so weiter)
.rk-lightbox-play-pauseControlsPlay/Pause-Button der Diashow (mit .rk-lightbox-btn)
.rk-lightbox-downloadControlsDownload-Button (mit .rk-lightbox-btn)
.rk-lightbox-closeControlsSchließen-Button
.rk-lightbox-counterControlsChip mit dem Bildzähler
.rk-lightbox-navNavigationNavigationspfeil (zurück und weiter)
.rk-lightbox-nav-prevNavigationPfeil zurück
.rk-lightbox-nav-nextNavigationPfeil weiter
.rk-lightbox-infoInfoContainer für Titel und Beschreibung
.rk-lightbox-info-titleInfoBildtitel
.rk-lightbox-info-descriptionInfoBildbeschreibung
.rk-lightbox-slideSlideSlide-Container
.rk-lightbox-imgSlideBildelement
.rk-lightbox-video-containerVideoSlideContainer eines Video-Slides (optional)
.rk-lightbox-video-elementVideoSlideVideoelement (optional)
.rk-lightbox-video-posterVideoSlidePosterbild 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.

TokenStandardSteuert
--rk-lightbox-overlay-bg#000Farbe des Hintergrunds
--rk-lightbox-overlay-z9999z-index des Overlays
--rk-lightbox-top-shade-height80pxHöhe des oberen Verlaufs
--rk-lightbox-top-shade-bglinear-gradient(rgba(0,0,0,0.6), transparent)Verlauf oben
--rk-lightbox-edge-padding16pxRandabstand für Schließen, Navigation und Controls
--rk-lightbox-btn-bgrgba(0, 0, 0, 0.5)Standard-Hintergrund für Schließen, Navigation und kleine Buttons
--rk-lightbox-btn-bg-hoverrgba(255, 255, 255, 0.2)Hover-Hintergrund für Schließen, Navigation und kleine Buttons
--rk-lightbox-btn-fg#fffIcon-Farbe für Schließen, Navigation und kleine Buttons
--rk-lightbox-btn-size36pxGröße der kleinen Buttons (Vollbild-Schalter und so weiter)
--rk-lightbox-btn-disabled-opacity0.5Deckkraft eines deaktivierten kleinen Buttons (Download läuft)
--rk-lightbox-close-size40pxGröße des Schließen-Buttons
--rk-lightbox-nav-size48pxGröße der Pfeile zurück und weiter
--rk-lightbox-nav-opacity0.7Deckkraft der Pfeile im Ruhezustand
--rk-lightbox-counter-bgrgba(0, 0, 0, 0.5)Hintergrund des Zähler-Chips
--rk-lightbox-counter-fg#fffTextfarbe des Zählers
--rk-lightbox-info-bglinear-gradient(transparent, rgba(0,0,0,0.8))Verlauf hinter der Bildunterschrift
--rk-lightbox-title-size18pxSchriftgröße des Titels
--rk-lightbox-description-size14pxSchriftgröße der Beschreibung
--rk-lightbox-video-bg#000Hintergrund der Letterbox hinter <video>
css
<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

TasteAktion
ArrowLeftVorheriges Bild
ArrowRightNächstes Bild
EscapeLightbox schließen (oder Vollbild verlassen, falls aktiv)