Vue Lightbox
Lightbox de galería de imágenes y vídeos a pantalla completa para Vue 3, con @reelkit/vue-lightbox.
Características
Instalación
npm install @reelkit/vue-lightbox @reelkit/vue lucide-vue-nextNo olvides importar los estilos:
import '@reelkit/vue-lightbox/styles.css';Iconos
Los controles por defecto usan lucide-vue-next para los iconos. Si prefieres otra librería de iconos, usa los scoped slots #controls y #navigation para poner los tuyos.
Inicio rápido
Importa la hoja de estilos y el componente LightboxOverlay, y controla la apertura y el cierre con 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
Seis scoped slots con nombre permiten personalizar por completo las superficies del overlay. Omite el slot para conservar el valor por defecto incluido; si no pones nada dentro del slot (por ejemplo, con v-if="false"), esa sección se oculta del todo.
| Slot | Ámbito | Descripción |
|---|---|---|
| #slide | SlideSlotScope | Sustituye el contenido de cada slide (necesario para los slides de vídeo) |
| #controls | ControlsSlotScope | Sustituye la barra de controles superior (cerrar, contador, pantalla completa) |
| #navigation | NavigationSlotScope | Sustituye las flechas de anterior y siguiente |
| #info | InfoSlotScope | Sustituye el overlay inferior con degradado del título y la descripción |
| #loading | LoadingSlotScope | Indicador de carga propio |
| #error | ErrorSlotScope | Indicador de error propio |
<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>Compatibilidad con vídeo
Los slides de vídeo son opcionales para que el bundle por defecto no incluya nada de audio ni vídeo. Llama a useVideoSlideRenderer(items) y pasa el VideoSlideRenderer / VideoControlsRenderer que devuelve a los slots #slide y #controls del overlay. Envuelve el overlay con el SoundProvider devuelto para que el botón de sonido incluido tenga contexto. El objeto devuelto tiene el tipo UseVideoSlideRendererResult: SoundProvider, hasVideo, VideoSlideRenderer y 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>El elemento <video> compartido que usan los slides de vídeo sigue el mismo patrón que el reel-player de Vue: en iOS la reproducción continúa entre cambios de slide sin necesitar un gesto del usuario en cada uno.
Pantalla completa
Usa useFullscreen de @reelkit/vue para observar o cambiar el estado de pantalla completa de un elemento referenciado. El lightbox controla su botón de pantalla completa incluido con el mismo 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>Estado en la URL
Ver la demo en vivo →Crea un controlador con useOverlayUrlState de @reelkit/vue y pásalo a LightboxUrlOverlay como controller, y la barra de direcciones controlará la galería: se abre sola cuando el parámetro nombra un slide y se cierra cuando el parámetro desaparece. Los enlaces se pueden compartir y el botón de volver cierra la galería. Es un componente distinto de LightboxOverlay, así que cada uno tiene una única forma de controlar el estado abierto: el modelo is-open o el controller de la URL, nunca los dos.
Claves incluidas
Puedes apuntar a los slides con una clave incluida: pasa con spread urlIndexKey (por posición) o urlStableIdKey (por un id estable) al controlador; las dos se reexportan desde @reelkit/vue. Consulta la guía del estado en la URL y la API del core.
Volver solo cierra cuando abriste la galería desde dentro de la aplicación: el enlace añadió una entrada, así que volver regresa a la galería. Un enlace compartido abierto directamente en una pestaña nueva no tiene historial detrás, así que el botón de volver del navegador sale del sitio; el botón de cerrar o Escape quitan el parámetro en el sitio y te dejan en la galería.
<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>El composable recibe un objeto de opciones y devuelve un UrlStateController (con set, index y value). Guárdalo para controlarlo por código: set es la escritura de bajo nivel que usa el overlay internamente (al cambiar de slide y con set(null) para cerrar). También maneja el overlay por código: set(index) lo abre, igual que navegar al parámetro. Aun así, es mejor abrirlo con un enlace: el href se puede compartir, se abre en una pestaña nueva y el botón de volver lo cierra, todo gratis y sin manejador.
Las opciones completas de useOverlayUrlState (param, adapter, codec, locator) están en la referencia de la API de Vue.
LightboxUrlOverlay solo recibe :controller (obligatorio), un emit @close y todas las props visuales y de comportamiento que reenvía LightboxOverlay (items, transition-fn, los scoped slots y demás), pero no is-open.
- Abrir cuesta una entrada en el historial; pasar slides la sustituye, así que cien deslizamientos no añaden ninguna y volver una vez siempre sale de la galería.
- Un enlace compartido como
?photo=3abre la galería en ese slide. Un parámetro que no nombra ningún slide se quita de la URL en lugar de apuntar a un slide que no se puede abrir.
En una aplicación con router, pasa un adaptador. Escribir directamente en el historial deja desfasada la ubicación del propio router, y su siguiente navegación pierde el parámetro.
<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>Enlaces estables. El índice es posicional, así que un marcador abre otra imagen en cuanto la lista se reordena. urlStableIdKey usa como clave el id estable de cada elemento y recorre la lista actual; una sola llamada cubre el caso habitual.
<script setup lang="ts">
const photo = useOverlayUrlState({
param: 'photo',
...urlStableIdKey({ items: () => images }),
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="images" />
</template>Pasa hashCodec: base64UrlCodec para codificar el id en base64url en la URL: es una ofuscación reversible, no un hash criptográfico.
Si usas otro campo como clave (un slug) o paginas un feed infinito con locateAsync, crea tú el codec y el locator: codec escribe la identidad en la URL y locator encuentra dónde está ahora.
<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>Galerías infinitas o paginadas. locate es síncrono, así que solo puede responder por los elementos ya cargados: un enlace compartido a la imagen 400 de un feed que ha cargado 20 no encuentra nada. locateAsync es la alternativa y solo se llama cuando falla: carga las páginas que necesites y devuelve el índice que resulte tener la identidad.
Atajo
¿Usas el id del elemento como clave? Sáltate el codec y el locator hechos a mano y pasa locateAsync directamente a urlStableIdKey({ items, locateAsync }) (carga cuando no lo encuentra y después devuelve el índice). La versión más completa de abajo es para usar otro campo como clave o para tener el control total.
<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>Mientras está pendiente, el lightbox sigue cerrado y el parámetro no se toca, así que el enlace directo sobrevive a la carga. null o un rechazo quitan el parámetro. Una respuesta que llega después de que la URL haya cambiado, después de cerrar o después de desmontar se descarta, así que una carga lenta no puede abrir un slide que nadie ha pedido. Lo que devuelva manda: indica el índice de los datos que acaba de cargar, y el lightbox lo usa tal cual en lugar de volver a leer items, que Vue todavía no ha vuelto a renderizar.
Avance automático
Activa auto-advance para reproducir la galería como un pase de diapositivas al abrirse. Está desactivado por defecto, así que un lightbox existente se renderiza y se comporta como antes.
<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>- Las imágenes se quedan
auto-advance-interval(5000 ms por defecto), contados desde que la imagen ha cargado, así que una imagen lenta nunca se salta antes de que nadie la vea. Una imagen que no carga cuenta desde su error. - Los vídeos se reproducen una vez en lugar de en bucle y pasan al siguiente cuando terminan.
VideoSlideRendererlo hace por ti; un#slidepropio leeautoAdvancede su ámbito e informa del final cononEnded. - Espera mientras el ratón reposa sobre un control (un botón, un enlace o el pie), durante un deslizamiento y mientras la página está oculta, y después continúa desde donde se detuvo.
- Al moverse a mano (flechas, teclas, rueda, deslizamiento) el intervalo del nuevo slide empieza desde cero; el pase de diapositivas sigue activo.
- En el último slide se detiene, salvo que
loopesté activado. El botón de reproducir/pausar de los controles muestra el estado y permite cambiarlo;play()ypause()de la LightboxApi hacen lo mismo desde código.
Un slot #controls propio recibe isAutoAdvancing y, solo cuando auto-advance está activado, onToggleAutoAdvance: renderiza un PlayPauseButton cuando esté presente. El cálculo de los tiempos es createSlideshowController de @reelkit/core; consulta la guía de reproducción automática y la API del core.
Descarga
Activa show-download para añadir un botón de descarga a los controles. Guarda el elemento activo desde su downloadUrl, o src si no lo tiene, con el nombre downloadName, o el último segmento de la URL si no lo tiene.
<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>El archivo se descarga con fetch y se guarda, porque el navegador ignora el atributo download en un enlace de otro origen y abriría el archivo en su lugar. Cuando el fetch falla —normalmente un servidor de otro origen que no envía cabeceras CORS—, el archivo se abre en una pestaña nueva, donde se puede guardar igualmente. Sirve los archivos con Access-Control-Allow-Origin para guardarlos con un solo clic. El botón está desactivado mientras hay una descarga en curso.
Un slot #controls propio recibe isDownloading y, solo cuando show-download está activado, onDownload: renderiza un DownloadButton cuando esté presente. El ayudante es downloadFile de @reelkit/core; consulta la guía de descarga.
Botones de control
| Componente | Props | Descripción |
|---|---|---|
PlayPauseButton | PlayPauseButtonProps: isPlaying, onPress, className? | Interruptor del pase de diapositivas (icono Play/Pause, aria-pressed mientras se reproduce). Lo muestran los controles por defecto con auto-advance. |
DownloadButton | DownloadButtonProps: isDownloading, onPress, className? | Botón de descarga, desactivado mientras isDownloading. Lo muestran los controles por defecto con show-download. |
CloseButton | CloseButtonProps: onPress, className? | Botón de cerrar por defecto que renderiza la barra de controles; colócalo dentro de tu propio slot #controls para conservar el aspecto incluido. |
Counter | CounterProps: currentIndex, count, className? | Chip contador "3 / 10" por defecto; colócalo dentro de tu propio slot #controls para conservar el aspecto incluido. |
FullscreenButton | FullscreenButtonProps: isFullscreen, onPress, className? | Botón de pantalla completa por defecto; su icono sigue a isFullscreen. |
SoundButton | SoundButtonProps: isMuted, onPress, className? | Botón de silencio por defecto para los elementos de vídeo; su icono sigue a isMuted. |
Piezas de slide y navegación
| Componente | Props | Descripción |
|---|---|---|
ImageSlide | ImageSlideProps: src, alt?, isActive, onReady?, onLoadError? | Slide de imagen por defecto que renderiza el overlay; colócalo dentro de tu propio slot #slide para conservar el aspecto incluido en los elementos de imagen. |
LightboxVideoSlide | LightboxVideoSlideProps: src, poster?, isActive, size, slideKey, loop?, onPlaying?, onWaiting?, onLoadError? | Slide de vídeo por defecto, que reproduce a través del elemento <video> compartido; useVideoSlideRenderer lo renderiza por ti. |
LightboxNavigation | LightboxNavigationProps: activeIndex, count, loop?, onPrev, onNext | Flechas de anterior y siguiente por defecto; colócalo dentro de tu propio slot #navigation para conservar el aspecto incluido. |
Referencia de la API
Props de LightboxOverlay
LightboxOverlayProps
| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
isOpen | boolean | obligatorio | Controla la visibilidad; con false el overlay se quita del DOM. Se puede enlazar con v-model:is-open. |
items | LightboxItem[] | obligatorio | Array de elementos (imágenes o vídeos) |
initialIndex | number | 0 | Índice, empezando en cero, del elemento visible al principio |
transitionFn | TransitionTransformFn | slideTransition | Función de transición entre slides. Importa una incluida (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) o pasa una propia. Si se omite, se usa slideTransition. |
showInfo | boolean | true | Indica si se renderiza el overlay de información con el título y la descripción |
showControls | boolean | true | Indica si se renderiza la barra de controles superior (cerrar, contador, pantalla completa) |
showNavigation | boolean | true | Indica si se renderizan las flechas de anterior y siguiente (solo en escritorio) |
transitionDuration | number | 300 | Duración de la animación de los slides en ms |
swipeDistanceFactor | number | 0.12 | Fracción mínima de la distancia de deslizamiento (0–1) para cambiar de slide |
swipeToCloseDirection | 'up' | 'down' | 'up' | Dirección del gesto de deslizar para cerrar en móvil |
loop | boolean | false | Indica si el slider pasa del último slide al primero |
enableNavKeys | boolean | true | Activa la navegación con las teclas de flecha |
enableWheel | boolean | true | Activa la navegación con la rueda del ratón |
wheelDebounceMs | number | 200 | Duración del debounce de los eventos de la rueda en ms |
autoAdvance | boolean | false | Reproduce la galería como un pase de diapositivas. Consulta Avance automático. |
autoAdvanceInterval | number | 5000 | Cuánto tiempo se queda cada imagen durante un pase de diapositivas, en ms, contado desde que ha cargado |
showDownload | boolean | false | Añade un botón de descarga para el elemento activo. Consulta Descarga. |
ariaLabel | string | 'Image gallery' | Etiqueta accesible de la región del diálogo |
Props de LightboxUrlOverlay
LightboxUrlOverlayProps
Acepta todas las props visuales y de comportamiento de arriba salvo is-open, que se sustituye por controller. Emite close, slide-change y api-ready, pero no update:is-open. initial-index se ignora aquí: la posición del controlador elige el slide, así que un valor pasado junto a él se sobrescribiría en cada apertura.
| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
controller | UrlStateController | obligatorio | Controlador de useOverlayUrlState. Su posición decide si el overlay está abierto y qué slide muestra; el overlay escribe a través de él al cambiar de slide y al cerrar. |
Eventos de LightboxOverlay
| Evento | Datos | Descripción |
|---|---|---|
close | void | Se emite cuando el usuario cierra el lightbox |
slide-change | number | Se emite con el índice del nuevo slide activo despu és de un cambio |
api-ready | LightboxApi | Se emite cuando el slider está listo y expone la API imperativa |
update:is-open | boolean | Se emite al cerrar; permite usar v-model:is-open |
LightboxApi
El objeto que emite api-ready, expuesto también en la ref de plantilla del componente. El slider solo existe mientras el overlay está abierto, así que cada método del slider no hace nada mientras está cerrado; close() siempre funciona.
| Método | Tipo | Descripción |
|---|---|---|
next() | () => void | Pasa a la siguiente slide, con animación. Se queda en la última salvo que loop esté activo. |
prev() | () => void | Vuelve a la slide anterior, con animación. Se queda en la primera salvo que loop esté activo. |
goTo(index, animate?) | (number, boolean?) => Promise<void> | Salta a una slide. index se acota a los items y animate es false por defecto. Se resuelve cuando termina el movimiento. |
adjust() | () => void | Recalcula la posición de la slide tras un cambio de layout que el overlay no detectó por sí mismo |
observe() | () => void | Vuelve a escuchar gestos, teclado y rueda del ratón después de unobserve() |
unobserve() | () => void | Deja de escuchar gestos, teclado y rueda del ratón; las slides se quedan donde están |
close() | () => void | Cierra el lightbox, igual que el botón de cerrar o Escape |
play() | () => void | Inicia el pase de diapositivas y continúa el slide activo donde se detuvo. No hace nada sin auto-advance. |
pause() | () => void | Detiene el pase de diapositivas; el slide activo conserva su progreso. No hace nada sin auto-advance. |
Interfaz LightboxItem
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
src | string | sí | URL de la imagen o el vídeo |
type | 'image' | 'video' | no | Tipo de elemento. Por defecto es 'image' |
poster | string | no | Imagen en miniatura de los vídeos |
title | string | no | Título que se muestra en el overlay de información |
description | string | no | Descripción que se muestra debajo del título |
width | number | no | Ancho intrínseco de la imagen en píxeles |
height | number | no | Alto intrínseco de la imagen en píxeles |
downloadUrl | string | no | Lo que guarda el botón de descarga. Por defecto es src |
downloadName | string | no | Nombre con el que se guarda el archivo. Por defecto es el último segmento de la ruta de la URL |
Tipos del ámbito de slot
| Tipo | Campos |
|---|---|
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 } |
Transiciones
Pasa cualquier TransitionTransformFn con la prop transition-fn. Si importas solo la transición que usas, el bundler puede descartar el resto con tree-shaking. Si se omite, se usa slideTransition.
| Función | Descripción |
|---|---|
slideTransition | Por defecto. Desplazamiento horizontal entre slides; se reexporta desde @reelkit/vue. |
lightboxFadeTransition | Fundido cruzado con un ligero desplazamiento horizontal. Propia de @reelkit/vue-lightbox. |
flipTransition | Volteo 3D sobre el eje Y; se reexporta desde @reelkit/vue. |
lightboxZoomTransition | El slide entrante escala del 70 % al 100 % con fundido. Propia de @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>Carga de contenido y gestión de errores
Cuando te encargas del renderizado con el slot #slide, el ámbito del slot ofrece tres callbacks del ciclo de vida para indicar el estado de carga. El lightbox sigue el estado de cada slide y muestra un spinner o un icono de error según corresponda. Un precargador de contenido guarda en caché las URLs rotas, así que al volver a un slide que falló no se reintenta.
Callbacks del ciclo de vida
| Callback | Tipo | Descripción |
|---|---|---|
onReady | () => void | Indica que el contenido del slide se ha cargado bien (por ejemplo, la imagen se ha decodificado) |
onWaiting | () => void | Indica que el contenido del slide se está cargando (muestra el spinner) |
onError | () => void | Indica que el contenido del slide no se ha podido cargar (muestra el icono de error) |
Conectar los callbacks en #slide
<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>Slot de carga propio
Usa el slot #loading para sustituir el spinner por defecto.
<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>Slot de error propio
Usa el slot #error para sustituir el icono de imagen rota por defecto.
<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>Clases CSS
Todas las clases CSS son normales (no scoped), así que puedes apuntar a ellas con selectores más específicos en una hoja de estilos cargada después de @reelkit/vue-lightbox/styles.css. Para cambiar colores, tamaños y z-index, es mejor usar las propiedades personalizadas de CSS de la sección Temas de más abajo.
| Clase | Componente | Descripción |
|---|---|---|
.rk-lightbox-overlay | Overlay | Contenedor raíz (fondo a pantalla completa) |
.rk-lightbox-top-shade | Overlay | Degradado superior detrás de los controles |
.rk-lightbox-spinner | Overlay | Spinner de carga por defecto |
.rk-lightbox-error | Overlay | Contenedor del estado de error (imagen rota) |
.rk-lightbox-error-text | Overlay | Texto del estado de error |
.rk-lightbox-controls-left | Controls | Contenedor de los controles de arriba a la izquierda |
.rk-lightbox-btn | Controls | Botón de control (pantalla completa, sonido, etc.) |
.rk-lightbox-play-pause | Controls | Botón de reproducir/pausar del pase de diapositivas (con .rk-lightbox-btn) |
.rk-lightbox-download | Controls | Botón de descarga (con .rk-lightbox-btn) |
.rk-lightbox-close | Controls | Botón de cerrar |
.rk-lightbox-counter | Controls | Chip del contador de imágenes |
.rk-lightbox-nav | Navigation | Flecha de navegación (anterior y siguiente) |
.rk-lightbox-nav-prev | Navigation | Flecha de anterior |
.rk-lightbox-nav-next | Navigation | Flecha de siguiente |
.rk-lightbox-info | Info | Contenedor del título y la descripción |
.rk-lightbox-info-title | Info | Título de la imagen |
.rk-lightbox-info-description | Info | Descripción de la imagen |
.rk-lightbox-slide | Slide | Contenedor del slide |
.rk-lightbox-img | Slide | Elemento de imagen |
.rk-lightbox-video-container | VideoSlide | Contenedor del slide de vídeo (opcional) |
.rk-lightbox-video-element | VideoSlide | Elemento de vídeo (opcional) |
.rk-lightbox-video-poster | VideoSlide | Imagen del póster del vídeo (opcional) |
Temas
Sobrescribe cualquier propiedad personalizada de CSS --rk-lightbox-* en :root (o en cualquier ancestro de .rk-lightbox-overlay) para cambiar el tema. Las declaraciones directas en .rk-lightbox-overlay taparían los valores heredados, así que pon los cambios en un selector ancestro.
| Token | Por defecto | Controla |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | Color del fondo |
--rk-lightbox-overlay-z | 9999 | z-index del overlay |
--rk-lightbox-top-shade-height | 80px | Altura del degradado superior |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | Degradado superior |
--rk-lightbox-edge-padding | 16px | Separación del borde de cerrar / navegación / controles |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | Fondo por defecto de los botones de cerrar / navegación / pequeños |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | Fondo al pasar el ratón de los botones de cerrar / navegación / pequeños |
--rk-lightbox-btn-fg | #fff | Color del icono de los botones de cerrar / navegación / pequeños |
--rk-lightbox-btn-size | 36px | Tamaño de los botones pequeños (pantalla completa, etc.) |
--rk-lightbox-btn-disabled-opacity | 0.5 | Opacidad de un botón pequeño desactivado (descarga en curso) |
--rk-lightbox-close-size | 40px | Tamaño del botón de cerrar |
--rk-lightbox-nav-size | 48px | Tamaño de las flechas de anterior / siguiente |
--rk-lightbox-nav-opacity | 0.7 | Opacidad en reposo de las flechas de anterior / siguiente |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | Fondo del chip del contador |
--rk-lightbox-counter-fg | #fff | Color del texto del contador |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | Degradado del fondo del pie |
--rk-lightbox-title-size | 18px | Tamaño de letra del título |
--rk-lightbox-description-size | 14px | Tamaño de letra de la descripción |
--rk-lightbox-video-bg | #000 | Fondo de las bandas detrás del <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>Accesibilidad
La raíz del overlay es un diálogo modal (role="dialog", aria-modal="true"). Usa la prop aria-label para cambiar lo que anuncia el lector de pantalla; por defecto es "Image gallery". Cada slide lleva role="group", aria-roledescription="slide" y un aria-label derivado de la posición (por ejemplo, "Image 2 of 5").
El lightbox captura el foco al abrirse y lo devuelve al disparador al cerrarse. Tab y Mayús+Tab recorren los elementos enfocables de dentro; el foco que sale (un clic fuera, un foco por código) vuelve dentro. Está implementado con captureFocusForReturn y createFocusTrap de @reelkit/vue.
Atajos de teclado
| Tecla | Acción |
|---|---|
ArrowLeft | Imagen anterior |
ArrowRight | Imagen siguiente |
Escape | Cierra el lightbox (o sale de la pantalla completa si está activa) |