WebberUI

Zoomable Density Grid

密度縮放網格:如 iOS 照片般切換每列欄數,所有格子以 FLIP 動畫平滑重排。

載入預覽⋯

Playground

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

2
8
<ZoomableDensityGrid />

安裝

npx shadcn@latest add https://webberui.com/r/zoomable-density-grid.json

或在 components.json 設定 registries 後,改用 @webberui/zoomable-density-grid 安裝。

安裝依賴後,從 registry JSON(/r/zoomable-density-grid.jsonfiles[0].content)複製 zoomable-density-grid.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

使用

import { ZoomableDensityGrid } from "@/components/ui/zoomable-density-grid";

<ZoomableDensityGrid densities={[2, 4, 6]} defaultDensity={4} gap={8}>
  {photos.map((src) => (
    <img
      key={src}
      src={src}
      alt=""
      className="aspect-square rounded-lg object-cover"
    />
  ))}
</ZoomableDensityGrid>

每個直接子元素會自動包進 motion.div 動畫格子,不需要額外標記。

Props

Prop型別預設值說明
densitiesnumber[][2, 4, 6]密度檔位(每檔=每列欄數),依序顯示在切換器上
defaultDensitynumberdensities[0]初始欄數,需為 densities 其中一檔
gapnumber8格子間距(px)
childrenReact.ReactNode網格內容,每個直接子元素自動包進動畫格子
onDensityChange(density: number) => void密度切換時觸發,帶入新的欄數
classNamestring附加到最外層容器的 class

細節

  • FLIP 重排:切換欄數時,每個格子以 Motion layout 動畫連續過渡位置與尺寸,而非淡出後重新出現
  • 微縮回彈:切換瞬間所有格子帶短暫的 scale 微縮回彈(spring),模擬 iOS 照片縮放的手感
  • 滑動底塊:切換器的選中底塊以 layoutId 在檔位之間滑動
  • 佈局動畫以 transform 實現,過渡期間格子內容會隨之短暫縮放;純色塊、圖片等內容視覺效果最佳
  • densities 傳入的檔位會自動取整、下限 1 並去重;空陣列時退回預設 [2, 4, 6]

可及性

  • 使用者系統開啟「減少動態效果」時,FLIP 與底塊滑動停用,切換時直接跳到新佈局(DOM 結構與 SSR 一致)
  • 切換器為原生 <button> 群組(role="group"aria-label),各檔位以 aria-pressed 標示選取狀態,並帶「每列 N 欄」的 aria-label
  • 鍵盤可 Tab 循序聚焦各檔位,focus-visible 時顯示外框

On this page