String Pinboard
紅線釘板版面:卡片像釘在軟木板上的線索,彼此以會自然下垂的紅線連接,拖動即時改變鬆緊與垂度。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
0.28
2
320
0.06
<StringPinboard />
安裝
npx shadcn@latest add https://webberui.com/r/string-pinboard.json或在 components.json 設定 registries 後,改用 @webberui/string-pinboard 安裝。
安裝依賴後,從 registry JSON(/r/string-pinboard.json 的 files[0].content)複製 string-pinboard.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
以 PinCard 擺放線索卡,connections 定義卡片之間的紅線。拖動任一張卡,所有相連的線會即時改變鬆緊與垂度。
import { StringPinboard, PinCard } from "@/components/ui/string-pinboard";
<StringPinboard
connections={[
["idea", "design"],
["design", "ship"],
]}
variant="cork"
height={360}
>
<PinCard id="idea" x={18} y={20} label="點子">
構想
</PinCard>
<PinCard id="design" x={52} y={16} label="設計">
設計
</PinCard>
<PinCard id="ship" x={76} y={64} label="上線">
上線
</PinCard>
</StringPinboard>;connections 也可用物件形式覆寫單一條線的顏色:
connections={[{ from: "idea", to: "design", color: "#0ea5e9" }]}Props
StringPinboard
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | ReactNode | — | 放入 PinCard 子元件 |
connections | PinConnection[] | [] | 卡片間的紅線;[from, to] 速記或 { from, to, color } |
positions | Record<string, PinPosition> | — | 受控座標,外部狀態同步進釘板(仍可拖曳) |
defaultPositions | Record<string, PinPosition> | — | 非受控初始座標,覆寫 PinCard 自身的 x/y |
onPositionsChange | (positions) => void | — | 任一卡片移動後觸發,帶入全部最新座標 |
variant | "cork" | "paper" | "slate" | "cork" | 釘板表面款式 |
stringColor | string | "#e11d48" | 紅線與圖釘顏色 |
sag | number | 0.28 | 線的鬆弛程度(0–1),越大垂得越深、拉遠時越快繃直 |
stringWidth | number | 2 | 線寬(px) |
height | number | 400 | 釘板高度(px) |
stagger | number | 0.06 | 卡片依序進場的間隔(秒) |
PinCard
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別 key,連線配對與座標記錄用 |
x | number | 50 | 初始 X 座標(百分比 0–100,指向卡片頂端圖釘) |
y | number | 50 | 初始 Y 座標(百分比 0–100,指向卡片頂端圖釘) |
label | string | id | 顯示名稱,用於無障礙標籤 |
children | ReactNode | — | 卡片內容 |
細節
- 懸鏈線垂度:每條線在初次量測時記下「靜止繩長」(以釘板對角線比例儲存),繩長固定不隨拖曳改變。當兩卡拉遠、弦長接近繩長時,線逐漸繃直;靠近時垂度增大,貼近真實掛繩的手感。
- 座標系統:卡片座標以百分比(0–100)表示,指向卡片頂端的圖釘;卡片自圖釘垂掛而下,因此縮放釘板時版面等比例維持。
- 受控/非受控雙模式:不傳
positions時由元件自行記錄座標;傳入positions則外部可驅動版面,拖曳與鍵盤移動仍會即時反映並透過onPositionsChange回報。 - 表面款式:
cork軟木斑點、paper方格紙格線、slate純石板,皆自適應深淺色。
可及性
- 每張卡片為
role="button"、可鍵盤聚焦,帶aria-roledescription="可拖曳的線索卡"與含連線數的aria-label。 - 聚焦卡片後,方向鍵可上下左右微調位置,移動同樣透過
onPositionsChange回報。 - 使用者系統開啟「減少動態效果」時,停用卡片進場與紅線描繪動畫,直接呈現最終版面;拖曳與鍵盤操作不受影響。
- 拖曳以指標事件實作並支援觸控(
touch-action: none),監聽器、requestAnimationFrame與ResizeObserver於卸載時全數清理。