Card Deck
可拖曳抽牌的堆疊卡組,四向甩牌、自動補位,支援無限循環或抽完見底兩種模式。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
3
96
500
<CardDeck />
安裝
npx shadcn@latest add https://webberui.com/r/card-deck.json或在 components.json 設定 registries 後,改用 @webberui/card-deck 安裝。
安裝依賴後,從 registry JSON(/r/card-deck.json 的 files[0].content)複製 card-deck.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
每個直接子節點就是一張卡片,第一項為初始最上層。容器需要有明確尺寸(卡片以絕對定位相互堆疊)。
import { CardDeck } from "@/components/ui/card-deck";
<div className="h-72 w-64">
<CardDeck className="h-full w-full">
<div className="flex h-full items-center justify-center">卡片一</div>
<div className="flex h-full items-center justify-center">卡片二</div>
<div className="flex h-full items-center justify-center">卡片三</div>
</CardDeck>
</div>程式化抽牌
透過 ref 取得 draw() / reset(),方向鍵盤與拖曳之外也能用按鈕觸發。
import { CardDeck, type CardDeckHandle } from "@/components/ui/card-deck";
const deckRef = React.useRef<CardDeckHandle>(null);
<CardDeck ref={deckRef}>{/* ... */}</CardDeck>
<button onClick={() => deckRef.current?.draw("left")}>抽牌</button>抽完見底
設定 loop={false} 時,卡片會被逐張抽掉,抽完後顯示 emptyState。
<CardDeck loop={false} emptyState={<p>沒有更多了</p>}>
{/* ... */}
</CardDeck>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 每個直接子節點是一張卡片,第一項為初始最上層 |
index | number | — | 受控的最上層卡片索引 |
defaultIndex | number | 0 | 非受控模式的初始最上層索引 |
onIndexChange | (index: number) => void | — | 最上層卡片變更時觸發 |
onDraw | (index: number, direction: DrawDirection) => void | — | 抽掉一張卡時觸發,帶入被抽卡片索引與方向 |
loop | boolean | true | true 抽後循環回牌底;false 抽完顯示空狀態 |
visibleCount | number | 3 | 同時堆疊渲染的層數(含最上層) |
drawThreshold | number | 96 | 觸發抽牌的拖曳位移閾值(px) |
velocityThreshold | number | 500 | 觸發抽牌的拖曳甩動速度閾值(px/s) |
emptyState | React.ReactNode | — | loop={false} 抽完後顯示的內容 |
cardClassName | string | — | 套用到每張卡片外觀表面的樣式 |
className | string | — | 套用到牌組容器 |
DrawDirection 為 "up" | "down" | "left" | "right"。
ref 會取得 CardDeckHandle:draw(direction?) 抽掉最上層卡片(預設向上),reset() 回到第一張。
細節
- 最上層卡片可自由拖曳(四個方向),位移或甩動速度超過閾值即朝該方向飛出,未達門檻則彈回原位。
- 拖曳時卡片會依水平位移量輕微傾斜;後排卡片以索引雜湊出穩定的堆疊傾角,看起來像自然疊放的一落牌。
- 抽牌後下一張自動 spring 補位到最前,牌組後方遞補新卡;
loop開啟時被抽的卡循環回牌底。 - 受控(傳入
index+onIndexChange)與非受控(defaultIndex)雙模式;子節點數量變動時非受控索引會自動夾回合法範圍。
可及性
- 最上層卡片可聚焦(
tabIndex),方向鍵對應四向抽牌,Enter/Space 向上抽出。 - 牌組容器帶
role="group"與aria-roledescription,並以aria-live區域朗讀目前張數/剩餘張數。 - 使用者系統開啟「減少動態效果」時,停用拖曳與飛出位移,抽牌改為淡出,避免大幅度動態。