WebberUI

燈箱藝廊(React)

縮圖牆點擊放大的全螢幕燈箱 React 元件:圖片從縮圖以共享元素 morph 進場,支援滾輪/捏合縮放與拖曳平移、雙擊放大、滑動與鍵盤切換、計數與說明列、底部縮圖列,Esc 或點背景關閉。

這是 WebberUI Pro 元件

上線活動期間免費:註冊或登入後,在上方預覽區按「複製安裝指令」就能直接安裝,不需付費、不用綁信用卡。下方那條指令未登入時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

縮圖牆(CSS grid、hover 輕放大、固定 4:3 比例)點一張就開全螢幕燈箱:大圖以共享元素從縮圖原位放大進場,關閉時縮回原位。燈箱內以 pointer 事件實作完整手勢——滾輪或雙指捏合以游標/兩指中點為中心縮放、放大後單指拖曳平移(範圍夾在圖片邊界內)、縮放為 1 時左右滑動超過 80px 切換、雙擊在 1 倍與 2 倍間切換;工具列有計數、放大/縮小與關閉,左右箭頭與 ←/→ 鍵換圖,底部有說明列與可點的縮圖列。開啟時鎖住頁面捲動、焦點移到關閉鈕並在燈箱內循環,關閉後焦點回到原縮圖。

載入預覽⋯
npx shadcn@latest add "https://webberui.com/r/lightbox-gallery.json?t=<安裝 token>"

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

3
4
<LightboxGallery />

安裝

npx shadcn@latest add "https://webberui.com/r/lightbox-gallery.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/lightbox-gallery 安裝。

使用

import { LightboxGallery, type LightboxImage } from "@/components/ui/lightbox-gallery";

const images: LightboxImage[] = [
  { id: "a", src: "/photos/a.jpg", thumb: "/photos/a-thumb.jpg", alt: "海邊日出", caption: "清晨五點的東岸" },
  { id: "b", src: "/photos/b.jpg", alt: "山谷雲海" },
  { id: "c", src: "/photos/c.jpg", alt: "老街午後", caption: "屋簷下的光影" },
];

// 非受控:點縮圖就開,元件自己管理開關與索引
<LightboxGallery images={images} columns={3} loop />

// 受控:由外部按鈕開到指定那一張
const [open, setOpen] = useState(false);
const [index, setIndex] = useState(0);
<LightboxGallery
  images={images}
  open={open}
  onOpenChange={setOpen}
  index={index}
  onIndexChange={setIndex}
  showThumbnails={false}
  zoomMax={6}
/>

Props

Prop型別預設值說明
imagesLightboxImage[]圖片清單;id 需唯一
columnsnumber3縮圖牆欄數
gapnumber8縮圖牆格距(px)
startIndexnumber0非受控模式的初始索引(首次掛載時的目前圖片)
indexnumber受控的目前索引;不提供時由元件內部管理
onIndexChange(index: number) => void目前索引變更時回呼(點縮圖、切換、滑動皆會觸發)
openboolean受控的燈箱開關;不提供時由元件內部管理
defaultOpenbooleanfalse非受控模式的初始開關
onOpenChange(open: boolean) => void燈箱開關變更時回呼(受控與非受控皆會觸發)
loopbooleanfalse是否在頭尾循環切換
showThumbnailsbooleantrue是否顯示燈箱底部縮圖列
showCounterbooleantrue是否顯示「3 / 12」計數
zoomMaxnumber4最大縮放倍率
labelsPartial<LightboxGalleryLabels>自訂介面文字(部分覆寫即可)
classNamestring透傳到最外層容器

LightboxImage

欄位型別預設值說明
idstring唯一識別碼;縮圖與燈箱大圖的共享元素動畫以此配對
srcstring大圖來源(需具備固有尺寸:一般點陣圖或帶 width/height 的 SVG)
altstring替代文字(無障礙必填),切換時也會被螢幕閱讀器朗讀
captionstring燈箱底部說明列文字
thumbstring縮圖來源;未提供時縮圖牆與燈箱縮圖列都用 src

LightboxGalleryLabels

欄位型別預設值說明
dialogstring"圖片燈箱"燈箱對話框的 aria-label
openstring"放大檢視"縮圖牆按鈕無障礙名稱的後綴,組成「替代文字(放大檢視)」
closestring"關閉"關閉按鈕
previousstring"上一張"上一張按鈕
nextstring"下一張"下一張按鈕
zoomInstring"放大"放大按鈕
zoomOutstring"縮小"縮小按鈕
thumbnailsstring"縮圖列"燈箱底部縮圖列的 aria-label

另外具名匯出 LightboxImageLightboxGalleryLabelsLightboxGalleryProps 型別。

細節

  • 共享元素進場:開啟燈箱那一張的大圖與縮圖共用 Motion layoutId,從縮圖原位放大進場;關閉時若仍停在同一張,會縮回原縮圖,切換過圖片後則淡出。之後切換的圖片以水平滑動進出場,切換時縮放與平移一併重置。
  • 手勢:以 pointer 事件實作,滑鼠、觸控、觸控筆行為一致。滾輪(含帶 ctrlKey 的觸控板捏合)以游標為中心縮放;雙指捏合追蹤兩個 pointer 的距離與中點,可邊縮放邊平移;縮放大於 1 時單指拖曳平移,平移範圍夾在圖片邊界內;縮放為 1 時單指水平位移超過 80px 切換(到頭尾且不循環時加阻力後彈回);300ms 內、24px 內的第二次點按視為雙擊,在 1 倍與 2 倍間切換;點按圖片以外的背景關閉。
  • 鍵盤:←/→ 切換、Home/End 跳頭尾、Esc 關閉、+- 縮放、0 回到 1 倍。
  • 尺寸:大圖以 max-width: 100%; max-height: 100% 等比縮放至舞台內,不放大小於舞台的圖;src 需要有固有尺寸(點陣圖皆有;SVG 請帶 widthheight)。開啟時會預先載入前後一張。
  • 傳送門與捲動鎖定:燈箱以 createPortal 掛在 document.body,避免被祖先的 transformoverflow 影響;開啟時在 effect 內把 document.body.style.overflow 設為 hidden,關閉或卸載時還原原值。

可及性

  • 燈箱為 role="dialog"aria-modal="true"aria-label 取自 labels.dialog,有說明文字時以 aria-describedby 指向說明列;開啟時焦點移到關閉鈕,Tab/Shift+Tab 在燈箱內循環,關閉後焦點回到原本點擊的縮圖
  • 縮圖牆每格都是 buttonaria-haspopup="dialog"),無障礙名稱為「替代文字(放大檢視)」;工具列、箭頭與縮圖列按鈕都有 aria-label,目前縮圖以 aria-current 標示;切換時 aria-live="polite" 區域朗讀「第 n 張,共 N 張:替代文字」
  • 所有互動皆有鍵盤對應(方向鍵切換、Esc 關閉、+-0 縮放),按鈕具 focus-visible 外框
  • 使用者系統開啟「減少動態效果」時,共享元素 morph 改為淡入淡出、水平滑動改為淡入淡出、縮放不做補間直接到位、縮圖列改為即時捲動,功能不受影響

本頁目錄