Vue Lightbox

@reelkit/vue-lightbox で作られた、Vue 3 用のフルスクリーンの画像と動画のギャラリーです。

ライブデモを見る →

機能

画像と動画
動画スライドを標準でサポート
タッチジェスチャー
スワイプで移動
スワイプで閉じる
上にスワイプして閉じる
キーボード操作
矢印キーと Escape
フルスクリーン
ブラウザー間で動く API
トランジション
スライド、フェード、フリップ、ズームイン
先読み
前後 2 枚を事前に取得
サウンド切り替え
スライドごとのミュート
読み込み状態
スピナーと独自のスロット
エラー処理
エラーアイコンと独自のスロット
スコープ付きスロット
カスタマイズできる 6 つのスロット領域
v-model
v-model:is-open の双方向バインディング
URL の状態
共有もブックマークもできるリンク
スライドショー
オプトインの自動送り、再生と一時停止に対応
ダウンロード
アクティブなアイテムを保存するオプトインのボタン

インストール

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

スタイルのインポートを忘れないでください:

typescript
import '@reelkit/vue-lightbox/styles.css';
アイコン

デフォルトのコントロールはアイコンに lucide-vue-next を使います。別のアイコンライブラリを使いたい場合は、スコープ付きスロットの #controls と #navigation で独自のものを渡してください。

クイックスタート

スタイルシートと LightboxOverlay コンポーネントをインポートし、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>

スコープ付きスロット

名前付きの 6 つのスコープ付きスロットで、オーバーレイの各部分を自由にカスタマイズできます。組み込みのデフォルトを使うならスロットを省略し、その部分を完全に隠すならスロットの中を空にします(たとえば v-if="false")。

スロットスコープ説明
#slideSlideSlotScope個々のスライドの内容を置き換えます(動画スライドに必要)
#controlsControlsSlotScope上部のコントロールバー(閉じる、カウンター、フルスクリーン)を置き換えます
#navigationNavigationSlotScope前後のナビゲーション矢印を置き換えます
#infoInfoSlotScope下部のタイトルと説明のグラデーションのオーバーレイを置き換えます
#loadingLoadingSlotScope独自の読み込み表示
#errorErrorSlotScope独自のエラー表示
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>

動画のサポート

動画スライドはオプトインなので、デフォルトのバンドルには音声や動画の処理が入りません。useVideoSlideRenderer(items) を呼び、返された VideoSlideRenderer/VideoControlsRenderer をオーバーレイの #slide と #controls のスロットに渡してください。組み込みのサウンド切り替えがコンテキストを持てるよう、返された SoundProvider でオーバーレイを包みます。返されるオブジェクトの型は UseVideoSlideRendererResult で、SoundProvider、hasVideo、VideoSlideRenderer、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>

動画スライドを支える共有の <video> 要素は、Vue のプレイヤーと同じパターンです。iOS では、スライドごとにユーザーの操作を求めることなく、スライドが変わっても再生が続きます。

フルスクリーン

@reelkit/vue の useFullscreen で、参照した要素のフルスクリーンの状態を監視したり切り替えたりします。ギャラリーの組み込みのフルスクリーンボタンも、同じコンポーザブルで動いています。

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 の状態

ライブデモを見る →

@reelkit/vue の useOverlayUrlState でコントローラーを作り、LightboxUrlOverlay に controller として渡すと、ギャラリーを持つのはアドレスバーになります。パラメーターがスライドを指すと自分で開き、パラメーターがなくなると閉じます。リンクは共有でき、戻るボタンでギャラリーが閉じます。これは LightboxOverlay とは別のコンポーネントなので、開閉状態を決めるものはそれぞれ 1 つだけです。is-open のモデルか、URL の controller のどちらかで、両方ではありません。

組み込みのキー

スライドは組み込みのキーで指せます。urlIndexKey(位置で指す)か urlStableIdKey(安定した id で指す)をコントローラーに展開してください。どちらも @reelkit/vue から再エクスポートされています。URL の状態のガイド と Core API を参照してください。

戻るで閉じるのは、アプリ内から開いた場合だけです。リンクがエントリーを積んだので、戻るとギャラリーの前へ戻ります。新しいタブで直接開いた共有リンクには前の履歴がないので、ブラウザーの戻るではサイトを離れます。閉じるボタンか Escape なら、その場でパラメーターを取り除いてギャラリーにとどまります。

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>

コンポーザブルは 1 つのオプションのオブジェクトを受け取り、UrlStateController(set、index、value を持ちます)を返します。プログラムから制御するために手元に残してください。set はオーバーレイが内部で使う低レベルの書き込みです(スライドの切り替えと、set(null) で閉じる処理)。プログラムからオーバーレイを操作することもでき、set(index) はパラメーターへ移動するのと同じように開きます。ただし、開く操作にはリンクを使うほうがよいでしょう。href は共有でき、新しいタブで開け、戻るボタンで閉じられます。ハンドラーなしで、すべてがそのまま手に入ります。

useOverlayUrlState のすべてのオプション(param、adapter、codec、locator)は Vue API リファレンス を参照してください。

LightboxUrlOverlay 自体が受け取るのは :controller(必須)、@close の emit、そして LightboxOverlay が渡すすべての見た目と挙動の props(items、transition-fn、スコープ付きスロットなど)です。is-open はありません。

  • 開くと履歴エントリーを 1 つ使います。スライドを送るとそれを置き換えるので、100 回スワイプしてもエントリーは増えず、1 回戻れば必ずギャラリーから出られます。
  • ?photo=3 のような共有リンクは、そのスライドでギャラリーを開きます。どのスライドも指さないパラメーターは、開けないスライドを指したままにならないよう、URL から取り除かれます。

ルーターを使うアプリでは、アダプターを渡します。 履歴に直接書き込むとルーター自身の位置情報が古くなり、次のナビゲーションでパラメーターが消えます。

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>

安定したリンク。 インデックスは位置なので、リストが並べ替えられるとブックマークは別の画像を開きます。urlStableIdKey は各アイテムの安定した id で指し、現在のリストを走査します。よくあるケースは 1 回の呼び出しで済みます。

vue
<script setup lang="ts">
const photo = useOverlayUrlState({
  param: 'photo',
  ...urlStableIdKey({ items: () => images }),
});
</script>

<template>
  <LightboxUrlOverlay :controller="photo" :items="images" />
</template>

hashCodec: base64UrlCodec を渡すと、URL の id を base64url でエンコードします。元に戻せる難読化で、暗号学的ハッシュではありません。

別のフィールド(slug)で指したい場合や、locateAsync で無限フィードをページングしたい場合は、codec/locator を自分で作ります。codec は識別子を URL に書き、locator はそれがいまどこにあるかを探します。

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>

無限またはページングされたギャラリー。 locate は同期なので、読み込み済みのアイテムにしか答えられません。20 件を読み込んだフィードの 400 番目の画像への共有リンクは見つかりません。locateAsync はフォールバックで、それが見つけられなかったときだけ呼ばれます。必要なページを読み込み、その識別子が最終的に持ったインデックスを返してください。

近道

アイテムの id で指すなら、codec と locator を手書きする必要はありません。locateAsync をそのまま urlStableIdKey({ items, locateAsync }) に渡してください(見つからなければ取得してからインデックスを返します)。下のより完全な例は、別のフィールドで指す場合や、すべてを自分で制御したい場合のものです。

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>

保留中のあいだギャラリーは閉じたままで、パラメーターはそのまま残るので、ディープリンクは取得のあいだも失われません。null か reject でパラメーターは取り除かれます。URL が変わったあと、閉じたあと、アンマウントしたあとに届いた答えは破棄されるので、遅い取得が誰も求めていないスライドを開くことはありません。返したものが正です。取得したばかりのデータのインデックスを伝えるもので、ギャラリーは Vue がまだ再描画していない items を読み直さずに、それをそのまま使います。

自動送り

auto-advance を指定すると、ギャラリーが開いたときにスライドショーとして再生します。デフォルトではオフなので、既存の Lightbox の描画と動作は変わりません。

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>
  • 画像は auto-advance-interval(デフォルトは 5000 ms)のあいだ表示されます。画像の読み込みが終わった時点から数えるので、遅い画像が誰にも見られないまま飛ばされることはありません。読み込みに失敗した画像は、そのエラーの時点から数えます。
  • 動画はループせずに 1 回だけ再生し、終わると次へ進みます。VideoSlideRenderer がこれを自動で行います。独自の #slide ではスコープから autoAdvance を読み、終わったことを onEnded で伝えてください。
  • 待機するのは、マウスがコントロール(ボタン、リンク、キャプション)の上にあるあいだ、スワイプ中、ページが非表示のあいだです。そのあとは止まったところから続けます。
  • 手で移動すると(矢印、キー、ホイール、スワイプ)、新しいスライドの間隔は 0 から始まります。スライドショーはオンのままです。
  • 最後のスライドでは、loop が指定されていない限り止まります。コントロールの再生/一時停止ボタンが状態を示し、閲覧者が切り替えられます。LightboxApi の play() と pause() で、同じことをコードから行えます。

独自の #controls スロットは isAutoAdvancing を受け取り、auto-advance がオンのときだけ onToggleAutoAdvance も受け取ります。それがあるときは PlayPauseButton を描画してください。タイミング自体は @reelkit/core の createSlideshowController です。自動再生ガイドと Core API を参照してください。

ダウンロード

show-download を指定すると、コントロールにダウンロードボタンが加わります。アクティブなアイテムを downloadUrl(なければ src)から、downloadName(なければ 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>

ファイルは fetch してから保存します。別オリジンのリンクでは、ブラウザーは download 属性を無視してファイルを開いてしまうからです。fetch が失敗したとき(よくあるのは CORS ヘッダーを送らない別オリジンのサーバーです)は、ファイルを新しいタブで開くので、閲覧者はそこから保存できます。ワンクリックで保存できるようにするには、ファイルを Access-Control-Allow-Origin 付きで配信してください。ダウンロード中はボタンが無効になります。

独自の #controls スロットは isDownloading を受け取り、show-download がオンのときだけ onDownload も受け取ります。それがあるときは DownloadButton を描画してください。ヘルパーは @reelkit/core の downloadFile です。ダウンロードガイドを参照してください。

コントロールのボタン

コンポーネントProps説明
PlayPauseButtonPlayPauseButtonProps: isPlaying, onPress, className?スライドショーの切り替えボタン(Play/Pause のアイコン、再生中は aria-pressed)。auto-advance のときにデフォルトのコントロールが表示します。
DownloadButtonDownloadButtonProps: isDownloading, onPress, className?ダウンロードボタン。isDownloading のあいだは無効になります。show-download のときにデフォルトのコントロールが表示します。
CloseButtonCloseButtonProps: onPress, className?コントロールバーが描画するデフォルトの閉じるボタン。組み込みの見た目を保つには、独自の #controls スロットの中に置きます。
CounterCounterProps: currentIndex, count, className?デフォルトの「3 / 10」のカウンター。組み込みの見た目を保つには、独自の #controls スロットの中に置きます。
FullscreenButtonFullscreenButtonProps: isFullscreen, onPress, className?デフォルトのフルスクリーン切り替え。アイコンは isFullscreen に従います。
SoundButtonSoundButtonProps: isMuted, onPress, className?動画の要素向けのデフォルトのミュート切り替え。アイコンは isMuted に従います。

スライドとナビゲーションの部品

コンポーネントProps説明
ImageSlideImageSlideProps: src, alt?, isActive, onReady?, onLoadError?オーバーレイが描画するデフォルトの画像スライド。画像の要素で組み込みの見た目を保つには、独自の #slide スロットの中に置きます。
LightboxVideoSlideLightboxVideoSlideProps: src, poster?, isActive, size, slideKey, loop?, onPlaying?, onWaiting?, onLoadError?共有の <video> 要素で再生するデフォルトの動画スライド。useVideoSlideRenderer が描画してくれます。
LightboxNavigationLightboxNavigationProps: activeIndex, count, loop?, onPrev, onNextデフォルトの前後の矢印。組み込みの見た目を保つには、独自の #navigation スロットの中に置きます。

API リファレンス

LightboxOverlay の Props

LightboxOverlayProps

Prop型デフォルト説明
isOpenboolean必須表示を制御します。false のときオーバーレイは DOM から取り除かれます。v-model:is-open でバインドできます。
itemsLightboxItem[]必須アイテム(画像または動画)の配列
initialIndexnumber0最初に表示するアイテムの 0 始まりのインデックス
transitionFnTransitionTransformFnslideTransitionスライドのトランジション関数。組み込みのもの(slideTransition、flipTransition、lightboxFadeTransition、lightboxZoomTransition)をインポートするか、独自のものを渡します。省略すると slideTransition になります。
showInfobooleantrueタイトルと説明の情報オーバーレイを描画するかどうか
showControlsbooleantrue上部のコントロールバー(閉じる、カウンター、フルスクリーン)を描画するかどうか
showNavigationbooleantrue前後のナビゲーション矢印を描画するかどうか(デスクトップのみ)
transitionDurationnumber300スライドのアニメーションの長さ(ms)
swipeDistanceFactornumber0.12スライドを切り替えるのに必要なスワイプ距離の最小の割合(0–1)
swipeToCloseDirection'up' | 'down''up'モバイルでスワイプして閉じるジェスチャーの方向
loopbooleanfalse最後のスライドから最初のスライドへ戻るかどうか
enableNavKeysbooleantrueキーボードの矢印キーによる操作を有効にします
enableWheelbooleantrueマウスホイールでの操作を有効にします
wheelDebounceMsnumber200ホイールイベントのデバウンス時間(ms)
autoAdvancebooleanfalseギャラリーをスライドショーとして再生します。自動送りを参照してください。
autoAdvanceIntervalnumber5000スライドショーで各画像を表示しておく長さ(ms)。読み込みが終わった時点から数えます
showDownloadbooleanfalseアクティブなアイテムのダウンロードボタンを加えます。ダウンロードを参照してください。
ariaLabelstring'Image gallery'ダイアログ領域のアクセシブルなラベル

LightboxUrlOverlay の Props

LightboxUrlOverlayProps

上の見た目と挙動の props をすべて受け取りますが、is-open の代わりに controller を使います。close、slide-change、api-ready は発行しますが、update:is-open は発行しません。ここでは initial-index は無視されます。スライドはコントローラーの位置で決まるので、一緒に渡した値は開くたびに上書きされてしまいます。

Prop型デフォルト説明
controllerUrlStateController必須useOverlayUrlState のコントローラー。その位置で、オーバーレイが開いているかどうかと、どのスライドを表示するかが決まります。オーバーレイはスライドの切り替え時と閉じるときに、これを通じて書き戻します。

LightboxOverlay のイベント

イベントペイロード説明
closevoidユーザーがギャラリーを閉じたときに発行されます
slide-changenumber切り替え後の新しいアクティブなスライドのインデックスとともに発行されます
api-readyLightboxApiスライダーの準備ができたときに発行され、命令的な API を公開します
update:is-openboolean閉じるときに発行され、v-model:is-open を可能にします

LightboxApi

api-ready が発行するオブジェクトで、コンポーネントのテンプレート ref からも取得できます。スライダーはオーバーレイが開いている間しか存在しないため、閉じている間はスライダーの各メソッドは何もしません。close() は常に動作します。

メソッド型説明
next()() => voidアニメーション付きで次のスライドへ移動します。loop が有効でなければ最後のスライドに留まります。
prev()() => voidアニメーション付きで前のスライドへ移動します。loop が有効でなければ最初のスライドに留まります。
goTo(index, animate?)(number, boolean?) => Promise<void>指定したスライドへジャンプします。index はアイテムの範囲に収められ、animate のデフォルトは false です。移動が終わると解決します。
adjust()() => voidオーバーレイ自身が検知できなかったレイアウト変更のあとに、スライドの位置を計算し直します
observe()() => voidunobserve() のあとに、ジェスチャー・キーボード・ホイール入力の監視を再開します
unobserve()() => voidジェスチャー・キーボード・ホイール入力の監視を止めます。スライドはそのままの位置に残ります
close()() => void閉じるボタンや Escape と同じように lightbox を閉じます
play()() => voidスライドショーを始めます。アクティブなスライドは止まったところから続きます。auto-advance がなければ何もしません。
pause()() => voidスライドショーを止めます。アクティブなスライドは進み具合を保ちます。auto-advance がなければ何もしません。

LightboxItem インターフェース

フィールド型必須説明
srcstringはい画像または動画の URL
type'image' | 'video'いいえアイテムの種類。デフォルトは 'image' です
posterstringいいえ動画アイテムのサムネイル画像
titlestringいいえ情報オーバーレイに表示するタイトル
descriptionstringいいえタイトルの下に表示する説明
widthnumberいいえ画像の本来の幅(ピクセル)
heightnumberいいえ画像の本来の高さ(ピクセル)
downloadUrlstringいいえダウンロードボタンが保存するもの。デフォルトは src
downloadNamestringいいえ保存するファイル名。デフォルトは URL のパスの最後の部分

スロットスコープの型

型フィールド
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 }

トランジション

任意の TransitionTransformFn を transition-fn プロップで渡します。使うトランジションだけをインポートすれば、残りはバンドラーがツリーシェイキングします。省略すると slideTransition になります。

関数説明
slideTransitionデフォルト。スライド間を横方向に移動します。@reelkit/vue から再エクスポートされています。
lightboxFadeTransitionわずかに横へずらしながらクロスフェードします。@reelkit/vue-lightbox 独自のものです。
flipTransitionY 軸を中心に 3D でめくります。@reelkit/vue から再エクスポートされています。
lightboxZoomTransition入ってくるスライドがフェードしながら 70% から 100% に拡大します。@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>

コンテンツの読み込みとエラー処理

#slide スロットで描画を引き受ける場合、読み込み状態を報告するための 3 つのライフサイクルのコールバックがスロットのスコープで使えます。ギャラリーはスライドごとの状態を追跡し、それに応じてスピナーかエラーアイコンを表示します。コンテンツのプリローダーは壊れた URL をキャッシュするので、失敗したスライドを再訪しても再試行はしません。

ライフサイクルのコールバック

コールバック型説明
onReady() => voidスライドの内容の読み込みに成功したことを伝えます(たとえば画像のデコードが終わった)
onWaiting() => voidスライドの内容が読み込み中またはバッファリング中であることを伝えます(スピナーを表示)
onError() => voidスライドの内容の読み込みに失敗したことを伝えます(エラーアイコンを表示)

#slide でコールバックをつなぐ

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>

独自の読み込みスロット

#loading スロットで、デフォルトのスピナーを置き換えます。

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>

独自のエラースロット

#error スロットで、デフォルトの壊れた画像のアイコンを置き換えます。

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 クラス

CSS クラスはすべて通常のクラス(スコープなし)なので、@reelkit/vue-lightbox/styles.css のあとに読み込むスタイルシートで、詳細度の高いセレクターを使って指定できます。色、サイズ、z-index を変えるなら、下の テーマ設定 で説明している CSS カスタムプロパティを使ってください。

クラスコンポーネント説明
.rk-lightbox-overlayOverlayルートのコンテナー(フルスクリーンの背景)
.rk-lightbox-top-shadeOverlayコントロールの後ろの上部のグラデーション
.rk-lightbox-spinnerOverlayデフォルトの読み込みスピナー
.rk-lightbox-errorOverlayエラー状態のコンテナー(壊れた画像)
.rk-lightbox-error-textOverlayエラー状態のテキストラベル
.rk-lightbox-controls-leftControls左上のコントロールのコンテナー
.rk-lightbox-btnControlsコントロールのボタン(フルスクリーン、サウンドなど)
.rk-lightbox-play-pauseControlsスライドショーの再生/一時停止ボタン(.rk-lightbox-btn と併用)
.rk-lightbox-downloadControlsダウンロードボタン(.rk-lightbox-btn と併用)
.rk-lightbox-closeControls閉じるボタン
.rk-lightbox-counterControls画像のカウンターのチップ
.rk-lightbox-navNavigationナビゲーションの矢印(前へ・次への両方)
.rk-lightbox-nav-prevNavigation前へ の矢印
.rk-lightbox-nav-nextNavigation次へ の矢印
.rk-lightbox-infoInfoタイトルと説明のコンテナー
.rk-lightbox-info-titleInfo画像のタイトル
.rk-lightbox-info-descriptionInfo画像の説明
.rk-lightbox-slideSlideスライドのコンテナー
.rk-lightbox-imgSlide画像要素
.rk-lightbox-video-containerVideoSlide動画スライドのコンテナー(オプトイン)
.rk-lightbox-video-elementVideoSlide動画要素(オプトイン)
.rk-lightbox-video-posterVideoSlide動画のポスター画像(オプトイン)

テーマ設定

テーマを変えるには、任意の --rk-lightbox-* CSS カスタムプロパティを :root(または .rk-lightbox-overlay の任意の祖先)で上書きします。.rk-lightbox-overlay に直接宣言すると継承された値を隠してしまうので、上書きは祖先のセレクターに置いてください。

トークンデフォルト制御するもの
--rk-lightbox-overlay-bg#000背景色
--rk-lightbox-overlay-z9999オーバーレイの z-index
--rk-lightbox-top-shade-height80px上部のグラデーションの高さ
--rk-lightbox-top-shade-bglinear-gradient(rgba(0,0,0,0.6), transparent)上部のグラデーション
--rk-lightbox-edge-padding16px閉じる、ナビゲーション、コントロールの端からの距離
--rk-lightbox-btn-bgrgba(0, 0, 0, 0.5)閉じる、ナビゲーション、小さいボタンのデフォルトの背景
--rk-lightbox-btn-bg-hoverrgba(255, 255, 255, 0.2)閉じる、ナビゲーション、小さいボタンのホバー時の背景
--rk-lightbox-btn-fg#fff閉じる、ナビゲーション、小さいボタンのアイコンの色
--rk-lightbox-btn-size36px小さいボタンの大きさ(フルスクリーン切り替えなど)
--rk-lightbox-btn-disabled-opacity0.5無効になった小さいボタンの不透明度(ダウンロード中)
--rk-lightbox-close-size40px閉じるボタンの大きさ
--rk-lightbox-nav-size48px前後の矢印の大きさ
--rk-lightbox-nav-opacity0.7前後の矢印の通常時の不透明度
--rk-lightbox-counter-bgrgba(0, 0, 0, 0.5)カウンターのチップの背景
--rk-lightbox-counter-fg#fffカウンターのテキストの色
--rk-lightbox-info-bglinear-gradient(transparent, rgba(0,0,0,0.8))キャプションの背景のグラデーション
--rk-lightbox-title-size18pxタイトルの文字サイズ
--rk-lightbox-description-size14px説明の文字サイズ
--rk-lightbox-video-bg#000<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>

アクセシビリティ

オーバーレイのルートはモーダルダイアログ(role="dialog"、aria-modal="true")です。aria-label プロップを指定すると、スクリーンリーダーの読み上げが変わります。デフォルトは「Image gallery」です。各スライドは role="group"、aria-roledescription="slide"、そして位置から作られる aria-label(たとえば「Image 2 of 5」)を持ちます。

ギャラリーは開くとフォーカスを取り込み、閉じるとトリガーに戻します。Tab と Shift+Tab は中のフォーカス可能な要素を循環し、外へ出たフォーカス(外側のクリック、プログラムからのフォーカス)は引き戻されます。@reelkit/vue の captureFocusForReturn と createFocusTrap で実装されています。

キーボードショートカット

キー動作
ArrowLeft前の画像
ArrowRight次の画像
Escapeギャラリーを閉じる(フルスクリーン中ならフルスクリーンを終了)