Vue Lightbox
Galeria de imagens e vídeos em tela cheia para Vue 3, construída sobre @reelkit/vue-lightbox.
Recursos
Instalação
npm install @reelkit/vue-lightbox @reelkit/vue lucide-vue-nextNão esqueça de importar os estilos:
import '@reelkit/vue-lightbox/styles.css';Ícones
Os controles padrão usam lucide-vue-next para os ícones. Se você prefere outra biblioteca de ícones, use os slots com escopo #controls e #navigation para fornecer os seus.
Início rápido
Importe a folha de estilo e o componente LightboxOverlay, e comande a abertura e o fechamento com 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>Slots com escopo
Seis slots nomeados com escopo permitem personalizar por completo as superfícies do overlay. Omita o slot para manter o padrão embutido; não coloque nada dentro do slot (com v-if="false", por exemplo) para esconder aquela seção inteira.
| Slot | Escopo | Descrição |
|---|---|---|
| #slide | SlideSlotScope | Substitui o conteúdo de cada slide (necessário para slides de vídeo) |
| #controls | ControlsSlotScope | Substitui a barra de controles do topo (fechar, contador, tela cheia) |
| #navigation | NavigationSlotScope | Substitui as setas de avançar e voltar |
| #info | InfoSlotScope | Substitui o gradiente de título e descrição na base |
| #loading | LoadingSlotScope | Indicador de carregamento próprio |
| #error | ErrorSlotScope | Indicador de erro próprio |
<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>Suporte a vídeo
Os slides de vídeo são opcionais, para que o bundle padrão fique livre de toda a fiação de áudio e vídeo. Chame useVideoSlideRenderer(items) e repasse o VideoSlideRenderer / VideoControlsRenderer devolvidos aos slots #slide e #controls do overlay. Envolva o overlay no SoundProvider devolvido, para que o botão de som embutido tenha contexto. O objeto devolvido é tipado como UseVideoSlideRendererResult: SoundProvider, hasVideo, VideoSlideRenderer e 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>O elemento <video> compartilhado que move os slides de vídeo segue o mesmo padrão do reel player do Vue — no iOS, a reprodução continua ao trocar de slide sem exigir um novo gesto do leitor a cada um.
Tela cheia
Use useFullscreen, de @reelkit/vue, para observar ou alternar o estado de tela cheia de um elemento referenciado. O botão de tela cheia embutido da galeria usa esse mesmo 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 na URL
Ver demonstração ao vivo →Monte um controlador com useOverlayUrlState, de @reelkit/vue, e entregue-o ao LightboxUrlOverlay como controller: a partir daí, quem manda na galeria é a barra de endereços. Ela se abre quando o parâmetro nomeia um slide e se fecha quando o parâmetro some. Os links dão para compartilhar, e o botão voltar fecha a galeria. É um componente separado do LightboxOverlay, então cada um carrega exatamente um comando do estado aberto — o modelo is-open ou o controller da URL, nunca os dois.
Chaves prontas
Você pode endereçar os slides com uma chave pronta — espalhe urlIndexKey (por posição) ou urlStableIdKey (por um id estável) no controlador — as duas reexportadas por @reelkit/vue. Veja o guia de estado na URL e a API do core.
Voltar só fecha quando você abriu de dentro do app — o link empilhou uma entrada, então voltar desempilha até a galeria. Um link compartilhado aberto direto em uma aba nova não tem histórico atrás de si, então o voltar do navegador sai do site; o botão de fechar ou o Escape removem o parâmetro ali mesmo e mantêm você na galeria.
<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>O composable recebe um objeto de opções e devolve um UrlStateController (com set, index, value). Guarde-o para o controle programático: set é a escrita de baixo nível que o overlay usa internamente (troca de slide, e set(null) para fechar). Ele também conduz o overlay por código — set(index) o abre, exatamente como navegar até o parâmetro. Ainda assim, prefira um link para abrir: o href dá para compartilhar, abre em nova aba e o botão voltar o fecha — tudo de graça, sem nenhum handler.
As opções completas de useOverlayUrlState (param, adapter, codec, locator) estão na referência da API para Vue.
O próprio LightboxUrlOverlay recebe apenas :controller (obrigatório), um emit @close e todas as props visuais e de comportamento que o LightboxOverlay repassa (items, transition-fn, os slots com escopo e assim por diante) — mas nenhum is-open.
- Abrir custa uma entrada no histórico; percorrer os slides a substitui, então cem deslizes não acrescentam nenhuma — um único passo para trás sempre sai da galeria.
- Um link compartilhado como
?photo=3abre a galeria naquele slide. Um parâmetro que não nomeia slide nenhum é retirado da URL, em vez de afirmar um slide que não abre.
Em um app com rotas, passe um adaptador. Escrever direto no histórico deixa a localização do roteador desatualizada, e a navegação seguinte derruba o 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>Links estáveis. O índice é posicional, então um favorito abre outra imagem assim que a lista muda de ordem. urlStableIdKey usa o id estável de cada item, varrendo a lista atual — uma chamada resolve o caso comum.
<script setup lang="ts">
const photo = useOverlayUrlState({
param: 'photo',
...urlStableIdKey({ items: () => images }),
});
</script>
<template>
<LightboxUrlOverlay :controller="photo" :items="images" />
</template>Passe hashCodec: base64UrlCodec para escrever o id em base64url na URL — é um disfarce reversível, não um hash criptográfico.
Quer endereçar por outro campo (um slug), ou paginar um feed infinito com locateAsync? Monte o codec e o locator você mesmo: o codec escreve a identidade na URL, o locator descobre onde ela está agora.
<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>Galerias infinitas ou paginadas. locate é síncrono, então só responde pelos itens já carregados — um link compartilhado para a imagem 400 de um feed que carregou 20 volta vazio. locateAsync é o plano B, chamado somente quando ele falha: carregue as páginas necessárias e devolva o índice que a identidade acabou tendo.
Atalho
Vai endereçar pelo id do item? Dispense o codec e o locator escritos à mão — passe locateAsync direto para urlStableIdKey({ items, locateAsync }) (ele busca quando falha e então devolve o índice). A versão mais completa abaixo serve para endereçar por outro campo, ou para ter controle 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>Enquanto a busca corre, a galeria fica fechada e o parâmetro é deixado em paz, então o link direto sobrevive. Um null ou uma rejeição derrubam o parâmetro. Uma resposta que chega depois de a URL ter mudado, depois de um fechamento ou depois da desmontagem é descartada, então uma busca lenta não pode abrir um slide que ninguém pediu. O que ela devolver é palavra final — informa o índice dos dados que acabou de buscar, e a galeria o aceita como está, sem reler items, que o Vue ainda não renderizou de novo.
Avanço automático
Defina auto-advance para tocar a galeria como uma apresentação assim que ela abre. Vem desligado, então uma galeria que já existe renderiza e 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>- Imagens ficam por
auto-advance-interval(5000 ms por padrão), contados a partir do momento em que a imagem carregou, então uma imagem lenta nunca é pulada antes de alguém vê-la. Uma imagem que falha ao carregar conta a partir do erro. - Vídeos tocam uma vez em vez de repetir e passam adiante quando terminam. O
VideoSlideRendererfaz isso por você; um#slidepróprio lêautoAdvancedo seu escopo e avisa o fim poronEnded. - Ela espera enquanto o mouse repousa sobre um controle (um botão, um link ou a legenda), durante um deslize e enquanto a página está oculta, e depois continua de onde parou.
- Navegar à mão (setas, teclas, roda do mouse, deslize) começa o intervalo do novo slide do zero; a apresentação continua ligada.
- No último slide ela para, a menos que
loopesteja definido. O botão de play/pause nos controles mostra o estado e deixa quem assiste alterná-lo;play()epause()na LightboxApi fazem o mesmo pelo código.
Um slot #controls próprio recebe isAutoAdvancing e, só quando auto-advance está ligado, onToggleAutoAdvance — renderize um PlayPauseButton quando ele estiver lá. A temporização em si é o createSlideshowController de @reelkit/core; veja o guia de reprodução automática e a API do core.
Download
Defina show-download para acrescentar um botão de download aos controles. Ele salva o item ativo a partir do seu downloadUrl, ou de src na falta dele, com o nome downloadName, ou com o último segmento da URL na falta dele.
<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>O arquivo é buscado e salvo, porque o navegador ignora o atributo download num link de outra origem e abriria o arquivo em vez de salvá-lo. Quando a busca falha — em geral um servidor de outra origem que não envia cabeçalhos CORS — o arquivo abre numa nova aba, onde quem assiste ainda pode salvá-lo. Sirva os arquivos com Access-Control-Allow-Origin para salvar com um clique. O botão fica desativado enquanto um download está em andamento.
Um slot #controls próprio recebe isDownloading e, só quando show-download está ligado, onDownload — renderize um DownloadButton quando ele estiver lá. O helper é o downloadFile de @reelkit/core; veja o guia de download.
Botões de controle
| Componente | Props | Descrição |
|---|---|---|
PlayPauseButton | PlayPauseButtonProps: isPlaying, onPress, className? | Alternador da apresentação (ícone Play/Pause, aria-pressed enquanto toca). Mostrado pelos controles padrão com auto-advance. |
DownloadButton | DownloadButtonProps: isDownloading, onPress, className? | Botão de download, desativado enquanto isDownloading. Mostrado pelos controles padrão com show-download. |
CloseButton | CloseButtonProps: onPress, className? | Botão de fechar padrão que a barra de controles renderiza; coloque-o dentro do seu próprio slot #controls para manter o visual embutido. |
Counter | CounterProps: currentIndex, count, className? | Chip contador "3 / 10" padrão; coloque-o dentro do seu próprio slot #controls para manter o visual embutido. |
FullscreenButton | FullscreenButtonProps: isFullscreen, onPress, className? | Botão de tela cheia padrão; o ícone acompanha isFullscreen. |
SoundButton | SoundButtonProps: isMuted, onPress, className? | Botão de mudo padrão para itens de vídeo; o ícone acompanha isMuted. |
Peças de slide e de navegação
| Componente | Props | Descrição |
|---|---|---|
ImageSlide | ImageSlideProps: src, alt?, isActive, onReady?, onLoadError? | Slide de imagem padrão que o overlay renderiza; coloque-o dentro do seu próprio slot #slide para manter o visual embutido nos itens de imagem. |
LightboxVideoSlide | LightboxVideoSlideProps: src, poster?, isActive, size, slideKey, loop?, onPlaying?, onWaiting?, onLoadError? | Slide de vídeo padrão, que toca pelo elemento <video> compartilhado; useVideoSlideRenderer o renderiza para você. |
LightboxNavigation | LightboxNavigationProps: activeIndex, count, loop?, onPrev, onNext | Setas padrão de anterior e próximo; coloque-o dentro do seu próprio slot #navigation para manter o visual embutido. |
Referência da API
Props do LightboxOverlay
LightboxOverlayProps
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
isOpen | boolean | obrigatório | Controla a visibilidade; com false, o overlay sai do DOM. Dá para ligar com v-model:is-open. |
items | LightboxItem[] | obrigatório | Array de itens (imagens ou vídeos) |
initialIndex | number | 0 | Índice, começando em zero, do item visível no início |
transitionFn | TransitionTransformFn | slideTransition | Função da transição entre slides. Importe uma das prontas (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) ou passe a sua. Sem esta prop, vale slideTransition. |
showInfo | boolean | true | Se a camada de título e descrição aparece |
showControls | boolean | true | Se a barra de controles do topo aparece (fechar, contador, tela cheia) |
showNavigation | boolean | true | Se as setas de avançar e voltar aparecem (somente no desktop) |
transitionDuration | number | 300 | Duração da animação do slide em ms |
swipeDistanceFactor | number | 0.12 | Fração mínima da distância de deslize (0–1) para trocar de slide |
swipeToCloseDirection | 'up' | 'down' | 'up' | Direção do gesto de deslizar para fechar no celular |
loop | boolean | false | Se o slider passa do último slide de volta ao primeiro |
enableNavKeys | boolean | true | Liga a navegação pelas setas do teclado |
enableWheel | boolean | true | Liga a navegação pela roda do mouse |
wheelDebounceMs | number | 200 | Duração do debounce dos eventos de roda em ms |
autoAdvance | boolean | false | Toca a galeria como uma apresentação. Veja Avanço automático. |
autoAdvanceInterval | number | 5000 | Quanto tempo cada imagem fica durante a apresentação, em ms, contado a partir do momento em que carregou |
showDownload | boolean | false | Acrescenta um botão de download para o item ativo. Veja Download. |
ariaLabel | string | 'Image gallery' | Rótulo acessível da região do diálogo |
Props do LightboxUrlOverlay
LightboxUrlOverlayProps
Aceita todas as props visuais e de comportamento acima, menos is-open, substituída por controller. Emite close, slide-change e api-ready, mas nenhum update:is-open. initial-index é ignorada aqui — quem escolhe o slide é a posição do controlador, então um valor passado ao lado dela seria sobrescrito a cada abertura.
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
controller | UrlStateController | obrigatório | Controlador vindo de useOverlayUrlState. A posição dele decide se o overlay está aberto e qual slide aparece; o overlay escreve de volta por ele na troca de slide e no fechamento. |
Eventos do LightboxOverlay
| Evento | Carga | Descrição |
|---|---|---|
close | void | Emitido quando o leitor fecha a galeria |
slide-change | number | Emitido com o novo índice do slide ativo depois de uma troca |
api-ready | LightboxApi | Emitido assim que o slider fica pronto, expondo a API imperativa |
update:is-open | boolean | Emitido no fechamento; é o que viabiliza o v-model:is-open |
LightboxApi
O objeto que api-ready emite, exposto também na ref de template do componente. O slider só existe enquanto o overlay está aberto, então cada método do slider não faz nada enquanto ele está fechado; close() sempre funciona.
| Método | Tipo | Descrição |
|---|---|---|
next() | () => void | Vai para o próximo slide, com animação. Fica no último slide a menos que loop esteja ativo. |
prev() | () => void | Volta para o slide anterior, com animação. Fica no primeiro slide a menos que loop esteja ativo. |
goTo(index, animate?) | (number, boolean?) => Promise<void> | Salta para um slide. index é limitado aos itens e animate é false por padrão. Resolve quando o movimento termina. |
adjust() | () => void | Recalcula a posição do slide após uma mudança de layout que o overlay não percebeu sozinho |
observe() | () => void | Volta a escutar gestos, teclado e roda do mouse depois de unobserve() |
unobserve() | () => void | Para de escutar gestos, teclado e roda do mouse; os slides ficam onde estão |
close() | () => void | Fecha o lightbox, igual ao botão de fechar ou Escape |
play() | () => void | Inicia a apresentação, continuando o slide ativo de onde parou. Não faz nada sem auto-advance. |
pause() | () => void | Para a apresentação; o slide ativo mantém seu progresso. Não faz nada sem auto-advance. |
Interface LightboxItem
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
src | string | sim | URL da imagem ou do vídeo |
type | 'image' | 'video' | não | Tipo do item. O padrão é 'image' |
poster | string | não | Imagem de miniatura dos itens de vídeo |
title | string | não | Título mostrado na camada de informações |
description | string | não | Descrição mostrada abaixo do título |
width | number | não | Largura original da imagem em pixels |
height | number | não | Altura original da imagem em pixels |
downloadUrl | string | não | O que o botão de download salva. O padrão é src |
downloadName | string | não | Nome do arquivo salvo. O padrão é o último segmento do caminho da URL |
Tipos de escopo dos slots
| 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 } |
Transições
Passe qualquer TransitionTransformFn pela prop transition-fn. Importar apenas a transição que você usa deixa o bundler descartar as outras. Sem esta prop, vale slideTransition.
| Função | Descrição |
|---|---|
slideTransition | O padrão. Deslocamento horizontal entre os slides; reexportada de @reelkit/vue. |
lightboxFadeTransition | Esmaecimento cruzado com um empurrãozinho horizontal. Vive em @reelkit/vue-lightbox. |
flipTransition | Virada 3D em torno do eixo Y; reexportada de @reelkit/vue. |
lightboxZoomTransition | O slide que entra vai de 70% a 100% de escala, com esmaecimento. Vive em @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>Carregamento de conteúdo e tratamento de erros
Quando você assume a renderização pelo slot #slide, três callbacks de ciclo de vida ficam disponíveis no escopo do slot para informar o estado de carregamento. A galeria acompanha o estado de cada slide e mostra o girador ou o ícone de erro conforme o caso. Um pré-carregador guarda em cache as URLs quebradas, então voltar a um slide que falhou pula a nova tentativa.
Callbacks de ciclo de vida
| Callback | Tipo | Descrição |
|---|---|---|
onReady | () => void | Avisa que o conteúdo do slide carregou (a imagem foi decodificada, por exemplo) |
onWaiting | () => void | Avisa que o conteúdo do slide está carregando (mostra o girador) |
onError | () => void | Avisa que o conteúdo do slide não carregou (mostra o ícone de erro) |
Ligando os callbacks no #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 carregamento próprio
Use o slot #loading para substituir o girador padrão.
<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 erro próprio
Use o slot #error para substituir o ícone padrão de imagem quebrada.
<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>Classes CSS
Todas as classes CSS são simples (sem escopo), então dá para alcançá-las com seletores de especificidade maior em uma folha de estilo carregada depois de @reelkit/vue-lightbox/styles.css. Para mudar cor, tamanho e z-index, prefira as propriedades CSS documentadas na seção Temas, abaixo.
| Classe | Componente | Descrição |
|---|---|---|
.rk-lightbox-overlay | Overlay | Contêiner raiz (fundo em tela cheia) |
.rk-lightbox-top-shade | Overlay | Gradiente do topo, atrás dos controles |
.rk-lightbox-spinner | Overlay | Girador de carregamento padrão |
.rk-lightbox-error | Overlay | Contêiner do estado de erro (imagem quebrada) |
.rk-lightbox-error-text | Overlay | Texto do estado de erro |
.rk-lightbox-controls-left | Controls | Contêiner dos controles no canto superior esquerdo |
.rk-lightbox-btn | Controls | Botão de controle (tela cheia, som e afins) |
.rk-lightbox-play-pause | Controls | Botão de play/pause da apresentação (com .rk-lightbox-btn) |
.rk-lightbox-download | Controls | Botão de download (com .rk-lightbox-btn) |
.rk-lightbox-close | Controls | Botão de fechar |
.rk-lightbox-counter | Controls | Etiqueta contadora de imagens |
.rk-lightbox-nav | Navigation | Seta de navegação (avançar e voltar) |
.rk-lightbox-nav-prev | Navigation | Seta de voltar |
.rk-lightbox-nav-next | Navigation | Seta de avançar |
.rk-lightbox-info | Info | Contêiner de título e descrição |
.rk-lightbox-info-title | Info | Título da imagem |
.rk-lightbox-info-description | Info | Descrição da imagem |
.rk-lightbox-slide | Slide | Contêiner do slide |
.rk-lightbox-img | Slide | Elemento de imagem |
.rk-lightbox-video-container | VideoSlide | Contêiner do slide de vídeo (opcional) |
.rk-lightbox-video-element | VideoSlide | Elemento de vídeo (opcional) |
.rk-lightbox-video-poster | VideoSlide | Imagem de pôster do vídeo (opcional) |
Temas
Sobrescreva qualquer propriedade CSS --rk-lightbox-* em :root (ou em qualquer ancestral de .rk-lightbox-overlay) para trocar o tema. Declarações direto em .rk-lightbox-overlay sombreariam os valores herdados, então mantenha as sobrescritas em um seletor ancestral.
| Token | Padrão | O que controla |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | Cor do fundo |
--rk-lightbox-overlay-z | 9999 | z-index do overlay |
--rk-lightbox-top-shade-height | 80px | Altura do gradiente do topo |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | Gradiente do topo |
--rk-lightbox-edge-padding | 16px | Recuo da borda para fechar, navegação e controles |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | Fundo padrão dos botões de fechar, navegação e dos pequenos |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | Fundo desses botões ao passar o mouse |
--rk-lightbox-btn-fg | #fff | Cor do ícone desses botões |
--rk-lightbox-btn-size | 36px | Tamanho dos botões pequenos (tela cheia e afins) |
--rk-lightbox-btn-disabled-opacity | 0.5 | Opacidade de um botão pequeno desativado (download em andamento) |
--rk-lightbox-close-size | 40px | Tamanho do botão de fechar |
--rk-lightbox-nav-size | 48px | Tamanho das setas de avançar e voltar |
--rk-lightbox-nav-opacity | 0.7 | Opacidade das setas em repouso |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | Fundo da etiqueta do contador |
--rk-lightbox-counter-fg | #fff | Cor do texto do contador |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | Gradiente que escurece o fundo da legenda |
--rk-lightbox-title-size | 18px | Tamanho da fonte do título |
--rk-lightbox-description-size | 14px | Tamanho da fonte da descrição |
--rk-lightbox-video-bg | #000 | Cor das faixas em volta do <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>Acessibilidade
A raiz do overlay é um diálogo modal (role="dialog", aria-modal="true"). Defina a prop aria-label para mudar o que o leitor de tela anuncia; o padrão é "Image gallery". Cada slide carrega role="group", aria-roledescription="slide" e um aria-label derivado da posição (por exemplo, "Image 2 of 5").
A galeria captura o foco ao abrir e o devolve ao gatilho ao fechar. Tab e Shift+Tab percorrem os elementos focáveis lá dentro; um foco que escapa (clique fora, foco por código) é trazido de volta. Implementado com captureFocusForReturn e createFocusTrap, de @reelkit/vue.
Atalhos de teclado
| Tecla | Ação |
|---|---|
ArrowLeft | Imagem anterior |
ArrowRight | Próxima imagem |
Escape | Fecha a galeria (ou sai da tela cheia, se estiver nela) |