Lightbox
@reelkit/react-lightbox を使った、フルスクリーンの画像と動画のギャラリーのコンポーネントです。
機能
インストール
スタイルのインポートを忘れないでください:
アイコン
デフォルトのコントロールはアイコンに lucide-react を使います。別のアイコンライブラリを使いたい場合は、renderControls と renderNavigation で独自のものを渡してください。
クイックスタート
LightboxOverlay コンポーネントは画像をフルスクリーンで表示します。LightboxItem オブジェクトの配列を渡し、null を取りうるインデックスで表示を制御します。
ライブデモ
サムネイルをクリックするとギャラリーが開きます。矢印キーかスワイプで移動してください。
動画スライド(オプトイン)
動画のサポートは完全にオプトインで、ツリーシェイキングできます。画像だけを使う場合、バンドルは 1 バイトも増えません。useVideoSlideRenderer をインポートし、その戻り値を LightboxOverlay につないでください。読み込み状態、サウンドの管理、動画のライフサイクルはフックが自動で処理します。
仕組み
- フックは
SoundProviderを返します。ミュートを機能させるには、オーバーレイをこれで包んでください - スライドがアクティブになると、動画が自動再生されます(デフォルトはミュート)
- iOS で音声を途切れさせないよう、共有の動画要素がスライド間で再利用されます
- 動画スライドには、リアクティブなミュート切り替えのサウンドボタンが自動で表示されます
type: 'video'のないアイテムは画像として描画されます(後方互換)
カスタマイズ
独自のコントロール
renderControls で、デフォルトの閉じるボタン、カウンター、フルスクリーン切り替えを置き換えます。公開されているサブコンポーネントと組み合わせてください:
独自の情報オーバーレイ
renderInfo でデフォルトのタイトルと説明のグラデーションを置き換えるか、renderInfo={() => null} を渡して完全に非表示にします:
独自のナビゲーション
renderNavigation で、デフォルトの前後の矢印を置き換えます:
独自のスライド
renderSlide でスライドの内容を完全に独自のものにします。デフォルトの画像スライドに戻すには null を返します:
コンテンツの読み込みとエラー処理
ギャラリーはスライドごとの読み込み中とエラーの状態を追跡します。読み込み中はスピナーを、失敗したメディアには壊れた画像のアイコンを表示します。エラーになった URL はキャッシュされるので、再訪すると再試行せずにすぐエラーを表示しま す。
ライフサイクルのコールバック
renderSlide を使う場合は、これらのコールバックを呼んで読み込み表示を制御します:
| コールバック | 呼ぶタイミング |
|---|---|
onReady | 画像が読み込まれたか、動画の再生が始まったとき。読み込み中とエラーの状態をクリアします。 |
onWaiting | 再生の途中で動画がバッファリングしているとき。読み込み表示を出します。 |
onError | コンテンツの読み込みに失敗したとき。エラーのオーバーレイを出し、その URL を壊れたものとしてキャッシュします。 |
独自の読み込みとエラーの UI
デフォルトのスピナーとエラーアイコンを、独自のコンポーネントに置き換えます:
URL の状態
ライブデモを見る →LightboxUrlOverlay は、開閉状態をアドレスバーに持つ別のコンポーネントです。@reelkit/react の useOverlayUrlState でコントローラーを作り、controller として渡します。パラメーターがスライドを指すとギャラリーは自分で開き、パラメーターがなくなると閉じます。リンクは共有でき、戻るボタンはページを離れる代わりにギャラリーを閉じます。
組み込みのキー
スライドは組み込みのキーで指せます。urlIndexKey(位置で指す)か urlStableIdKey(安定した id で指す)をコントローラーに展開してください。どちらも @reelkit/react から再エクスポートされています。URL の状態のガイド と Core API を参照してください。
フックは 1 つのオプションのオブジェクトを受け取り、UrlStateController(set、index、value を持ちます)を返します。プログラムから制御するために手元に残してください。set はオーバーレイが内部で使う低レベルの書き込みです(スライドの切り替えと、set(null) で閉じる処理)。プログラムからオーバーレイを操作することもでき、set(index) はパラメーターへ移動するのと同じように開きます。ただし、開く操作にはリンクを使うほうがよいでしょう。href は共有でき、新しいタブで開け、戻るボタンで閉じられます。ハンドラーなしで、すべてがそのまま手に入ります。
useOverlayUrlState のすべてのオプション(param、adapter、codec、locator)は React API リファレンス を参照してください。
LightboxUrlOverlay 自体が受け取るのは controller(必須)、任意の onClose、そして LightboxOverlay が受け取るすべての見た目と挙動の props(images、ariaLabel、transitionFn、各レンダープロップなど)です。isOpen はありません。
- 開くと履歴エントリーを 1 つ使います。スライドを送るとそれを置き換えるので、100 回スワイプしてもエントリーは増えず、1 回戻れば必ずギャラリーから出られます。戻るとギャラリーが閉じ、写真は戻りません。
?photo=3のような共有リンクは、そのスライドでギャラリーを開きます。ページと一緒に届いたリンクを閉じると、サイトから離れるのではなく、その場でパラメーターを取り除きます。- 戻るで閉じるのは、アプリ内から開いた場合だけです。 リンクがエントリーを積んだので、戻るとギャラリーの前へ戻ります。新しいタブで直接開いた共有リンクには前の履歴がないので、ブラウザーの戻るではサイトを離れます。閉じるボタンか Escape なら、その場でパラメーターを 取り除いてギャラリーにとどまります。同梱のルーターアダプターは同じページでのリンクの push を保証します。エントリーの経緯を伝えられない独自のアダプターでもその場で閉じる動作になり、履歴にページの重複が残ります。その場合 1 回戻っても何も起きないように見えますが、再び開くことはありません。
- どのスライドも指さないパラメーター(古いブックマークや手で書き換えた値)は、開けないスライドをアドレスバーが指したままにならないよう、URL から取り除かれます。
ルーターを使うアプリでは、アダプターを渡します。 履歴に直接書き込むとルーター自身の位置情報が古くなり、次のナビゲーションでパラメーターが消えます。
開く操作はリンクです。 開閉状態が URL にあるので、サムネイルはただのリンクで、クリックハンドラーはいりません。新しいタブで開く、アドレスをコピーする、ホバーでプレビューするといったブラウザー本来の動作がそのまま使えます。ルーターを使うアプリでは、クライアント側で遷移するよう、ルーターのリンクを使ってください。
共有リンクには安定した識別子を使いましょう。 インデックスは位置なので、ブックマークした ?photo=3 はリストが並べ替えられると別の画像を開きます。urlStableIdKey は各アイテムの安定した id で指し、現在のリストを走査します。よくあるケースは 1 回の呼び出しで済みます。
hashCodec: base64UrlCodec を渡すと、URL の id を base64url でエンコードします。元に戻せる難読化で、暗号学的ハッシュではありません。
別のフィールド(slug)で指したい場合や、locateAsync で無限フィードをページングしたい場合は、codec/locator を自分で作ります:
無限またはページングされたギャラリー。 同期の locate は読み込み済みの画像にしか答えられません。20 件を読み込んだフィードの 400 番目の画像への共有リンクは見つかりません。locateAsync はフォールバックで、locate が見つけられなかったときだけ呼ばれます。
近道
アイテムの id で指すなら、codec と locator を手書きする必要はありません。locateAsync をそのまま urlStableIdKey({ items, locateAsync }) に渡してください(見つからなければ取得してからインデックスを返します)。下のより完全な例は、別のフィールドで指す場合や、すべてを自分で制御したい場合のものです。
- 読み込み方はあなた次第です。目的の位置まで連続したページを取得しても、その 1 枚だけを取得して追加してもかまいません。URL は位置ではなく識別子で指すので、
findIndexはアイテムが置かれた場所を返します。 locateAsyncが保留中のあいだ、ギャラリーは閉じたままで、パラメーターはそのまま残るので、ディープリンクは取得のあいだも失われません。nullか reject でパラメーターは取り除かれます。- URL が変わったあと、閉じたあと、アンマウントしたあとに届いた答えは破棄されます。遅い取得が、誰も求めていないスライドを開くことはありません。
- 保留中は何も描画されません。その読み込み状態はすでにページが持っているので、独自のスケルトンを描画してください。
- タイムアウトはありません。ギャラリーには自分の長さがわからないからです。ページングが尽きたら
nullで決着をつけてください。そうしないとオーバーレイは閉じたままになります。 locateAsyncが返したものが正です。取得したばかりのデータのインデックスとして、imagesを読み直さずにそのまま使われます。
API リファレンス
LightboxOverlay の Props
LightboxOverlayProps
| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
isOpen | boolean | 必須 | ギャラリーの表示を制御します。URL で開閉を決めたい場合は、別のコンポーネントの LightboxUrlOverlay を使ってください(下の URL の状態を参照)。 |
images | LightboxItem[] | 必須 | 表示する画像の配列 |
ariaLabel | string | 'Image gallery' | ダイアログ領域のアクセシブルなラベル。ギャラリーが開くとスクリーンリーダーが読み上げます |
initialIndex | number | 0 | 開始時の画像のインデックス |
transitionFn | TransitionTransformFn | slideTransition | スライドのトランジション関数。組み込みのもの(slideTransition、flipTransition、lightboxFadeTransition、lightboxZoomTransition)をインポートするか、独自のものを渡します。省略すると slideTransition になります。 |
apiRef | MutableRefObject<ReelApi> | - | Reel の API にアクセスするための ref |
renderControls | (props: ControlsRenderProps) => ReactNode | - | 独自のコントロール。デフォルトの閉じるボタン、カウンター、フルスクリーン切り替えを置き換えます |
renderNavigation | (props: NavigationRenderProps) => ReactNode | - | 独自のナビゲーション。デフォルトの前後の矢印を置き換えます |
renderInfo | (props: InfoRenderProps) => ReactNode | - | 独自の情報オーバーレイ。デフォルトのタイトルと説明のグラデーションを置き換えます。非表示にするには null を返します。 |
renderSlide | (props: SlideRenderProps) => ReactNode | null | - | 独自のスライド描画。{ item, index, size, isActive, onReady, onWaiting, onError } を受け取ります。デフォルトに戻すには null を返します。 |
renderLoading | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | 独自の読み込み表示。デフォルトのスピナーを置き換えます |
renderError | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | 独自のエラー表示。デフォルトのエラーアイコンを置き換えます |
LightboxUrlOverlay の Props
LightboxUrlOverlayProps
上の見た目と挙動の props をすべて受け取りますが、isOpen の代わりに controller を使います。ここでは initialIndex は無視されます。スライドはコントローラーの位置で決まるので、一 緒に渡した値は開くたびに上書きされてしまいます。
| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
controller | UrlStateController | 必須 | useOverlayUrlState のコントローラー。その位置で、オーバーレイが開いているかどうかと、どのスライドを表示するかが決まります。オーバーレイはスライドの切り替え時と閉じるときに、これを通じて書き戻します。 |
コールバック
| Prop | 型 | 説明 |
|---|---|---|
onClose | () => void | ギャラリーが閉じたときに呼ばれます。LightboxOverlay では必須です(開閉状態を持つのはあなたなので、閉じる処理も必要です)。LightboxUrlOverlay では任意で、閉じるのは URL が行うので、閉じたあとに反応したいときだけ渡します。 |
onSlideChange | (index: number) => void | スライドが切り替わったあとに呼ばれます |
Reel の Props(引き継ぎ)
これらの props は、内部の Reel コンポーネントに渡されます。
| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
loop | boolean | false | 無限ループを有効にします |
enableNavKeys | boolean | true | キーボード操作を有効にします |
enableWheel | boolean | true | マウスホイールでの操作を有効にします |
wheelDebounceMs | number | 200 | ホイールのデバウンス時間(ms) |
transitionDuration | number | 300 | トランジションのアニメーションの長さ(ms) |
swipeDistanceFactor | number | 0.12 | スワイプのしきい値(0-1) |
swipeToCloseDirection | 'up' | 'down' | 'up' | モバイルでスワイプして閉じるジェスチャーの方向 |
型
LightboxItem
ControlsRenderProps
NavigationRenderProps
SlideRenderProps
InfoRenderProps
サブコンポーネント
renderControls で独自のコントロールを組み立てるための、再利用できるサブコンポーネントです。
CloseButton
デフォルトの X の閉じるボタンです。
Counter
「1 / 3」と表示する画像のカウンターです。
FullscreenButton
フルスクリーンの切り替えボタンです(最大化/最小化のアイコン)。
SoundButton
動画スライド用のミュート切り替えボタンです(Volume2/VolumeX のアイコン)。useVideoSlideRenderer の renderControls には自動で含まれます。独自のコントロールの中で単独で使う場合は、useSoundState でサウンドの状態にアクセスしてください。
フック
useVideoSlideRenderer
オプトインの動画サポートのためのフックです。renderSlide、renderControls、SoundProvider を返します。オーバーレイを SoundProvider で包み、描画関数を渡してください。
useFullscreen
移動しました
useFullscreen は @reelkit/react-lightbox から削除されました。代わりに @reelkit/react からインポートしてください。
ブラウザー間で動くフルスクリーンの状態を管理するフックです。
トランジション
任意の TransitionTransformFn を transitionFn プロップで渡します。使うトランジションだけをインポートすれば、残りはバンドラーがツリーシェイキングします。省略すると slideTransition になります。
| 関数 | 提供元 | 説明 |
|---|---|---|
slideTransition | @reelkit/react-lightbox | 標準的な横方向のスライド(デフォルト) |
lightboxFadeTransition | @reelkit/react-lightbox | 画像どうしをクロスフェード |
flipTransition | @reelkit/react-lightbox | 3D でカードをめくる効果 |
lightboxZoomTransition | @reelkit/react-lightbox | 小さいサイズから通常のサイズへズームイン |
独自のトランジション関数
独自の TransitionTransformFn を書き、transitionFn で渡します。シグネチャはコアのスライダーのトランジションと同じです。
CSS クラス
UI 要素はすべて通常の CSS クラス(CSS Modules ではありません)を使うので、@reelkit/react-lightbox/styles.css のあとに読み込むスタイルシートで、詳細度の高いセレクターを使って指定できます。色、サイズ、z-index を変えるなら、下の テーマ設定 で説明している CSS カス タムプロパティを使ってください。
| クラス | コンポーネント | 説明 |
|---|---|---|
.rk-lightbox-overlay | Overlay | ルートのコンテナー(フルスクリーンの背景) |
.rk-lightbox-spinner | Overlay | デフォルトの読み込みスピナー |
.rk-lightbox-img-error | Overlay | エラー状態のコンテナー(壊れた画像や動画) |
.rk-lightbox-img-error-text | Overlay | エラー状態のテキストラベル |
.rk-lightbox-swipe-hint | Overlay | モバイルでのスワイプのヒント |
.rk-lightbox-controls-left | Controls | 左上のコントロールのコンテナー |
.rk-lightbox-btn | Controls | コントロールのボタン(フルスクリーンなど) |
.rk-lightbox-close | Controls | 閉じるボタン |
.rk-lightbox-counter | Controls | 画像のカウンターのチップ |
.rk-lightbox-nav | Navigation | ナビゲーションの矢印(両方) |
.rk-lightbox-nav-prev | Navigation | 前へ の矢印 |
.rk-lightbox-nav-next | Navigation | 次へ の矢印 |
.rk-lightbox-info | Info | タイトルと説明のコンテナー |
.rk-lightbox-title | Info | 画像のタイトル |
.rk-lightbox-description | Info | 画像の説明 |
.rk-lightbox-slide | Slide | スライドのコンテナー |
.rk-lightbox-img | Slide | 画像要素 |
.rk-lightbox-video-container | VideoSlide | 動画スライドのコンテナー(オプトイン) |
.rk-lightbox-video-element | VideoSlide | 動画要素(オプトイン) |
.rk-lightbox-video-poster | VideoSlide | 動画のポスター画像(オプトイン) |
テーマ設定
色、サイズ、z-index、トランジションはすべて CSS カスタムプロパティにあります。:root(またはギャラリーの任意の祖先)で 1 つでも複数でも上書きすれば、コンポーネントのソースに触れずにテーマを変えられます。
| トークン | デフォルト | 制御するもの |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | フルスクリーンの背景色 |
--rk-lightbox-overlay-z | 9999 | オーバーレイの z-index |
--rk-lightbox-top-shade-height | 80px | 上部のグラデーションの高さ |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | 上部のグラデーションの色 |
--rk-lightbox-edge-padding | 16px | 閉じる、ナビゲーション、左上のコントロールの端からの距離 |
--rk-lightbox-controls-gap | 12px | 左上のコントロールの間隔 |
--rk-lightbox-transition | 0.2s | ボタンのホバーのトランジションの長さ |
--rk-lightbox-blur | 8px | ボタンやチップの背景のぼかしの半径 |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | 閉じる、ナビゲーション、小さいボタンのデフォルトの背景 |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | 閉じる、ナビゲーション、小さいボタンのホバー時の背景 |
--rk-lightbox-btn-fg | #fff | 閉じる、ナビゲーション、小さいボタンのアイコンの色 |
--rk-lightbox-btn-size | 36px | 小さいボタンの大きさ(フルスクリーン切り替えなど) |
--rk-lightbox-close-size | 40px | 閉じるボタンの大きさ |
--rk-lightbox-nav-size | 48px | 前後の矢印の大きさ |
--rk-lightbox-nav-opacity | 0.7 | 前後の矢印の通常時の不透明度 |
--rk-lightbox-counter-fg | #fff | カウンターのテキストの色 |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | カウンターのチップの背景 |
--rk-lightbox-counter-size | 14px | カウンターの文字サイズ |
--rk-lightbox-counter-padding | 6px 12px | カウンターのチップの余白 |
--rk-lightbox-counter-radius | 20px | カウンターのチップの角丸 |
--rk-lightbox-spinner-size | 28px | デフォルトのスピナーの幅と高さ |
--rk-lightbox-spinner-track | rgba(255, 255, 255, 0.2) | スピナーのトラックの色 |
--rk-lightbox-spinner-fg | #fff | スピナーのインジケーターの色 |
--rk-lightbox-spinner-duration | 0.8s | スピナーの回転の長さ |
--rk-lightbox-error-fg | rgba(255, 255, 255, 0.4) | エラーアイコンとテキストの色 |
--rk-lightbox-error-text-size | 13px | エラーメッセージの文字サイズ |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | キャプションの背景のグラデーション |
--rk-lightbox-info-padding | 24px | キャプションの内側の余白 |
--rk-lightbox-title-size | 18px | タイトルの文字サイズ |
--rk-lightbox-description-size | 14px | 説明の文字サイズ |
--rk-lightbox-info-fg | #fff | キャプションのテキストの色 |
--rk-lightbox-hint-fg | rgba(255, 255, 255, 0.5) | スワイプのヒントのテキストの色 |
--rk-lightbox-hint-bg | rgba(0, 0, 0, 0.3) | スワイプのヒントのチップの背景 |
--rk-lightbox-hint-duration | 3s | スワイプのヒントのフェードイン・アウト全体の長さ |
--rk-lightbox-video-bg | #000 | <video> の後ろのレターボックスの背景 |
下のスニペットを、@reelkit/react-lightbox/styles.css のあとに読み込むスタイルシートに入れてください。
アクセシビリティ
オーバーレイのルートはモーダルダイアログ(role="dialog"、aria-modal="true")です。ariaLabel を指定すると、スクリーンリーダーの読み上げが変わります。デフォルトは「Image gallery」です。各スライドは role="group"、aria-roledescription="slide"、aria-label="Image N of M" を持ちます。
ギャラリーは開くとフォーカスを取り込み、閉じるとトリガーに戻します。Tab と Shift+Tab は中のフォーカス可能な要素を循環し、外へ出たフォーカス(外側のクリック、プログラムからのフォーカス)は引き戻されます。@reelkit/core の captureFocusForReturn と createFocusTrap で実装されています。
キーボードショートカット
| キー | 動作 |
|---|---|
ArrowLeft | 前の画像 |
ArrowRight | 次の画像 |
Escape | ギャラリーを閉じる(フルスクリーン中ならフルスクリーンを終了) |