Draggable Snap Button
可拖曳並回彈吸附的按鈕:拖到哪、放開後就以 spring 吸附到最近的落點,支援多段落點、方向鍵切換與純回彈模式。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
0.16
<DraggableSnapButton />
安裝
npx shadcn@latest add https://webberui.com/r/draggable-snap-button.json或在 components.json 設定 registries 後,改用 @webberui/draggable-snap-button 安裝。
安裝依賴後,從 registry JSON(/r/draggable-snap-button.json 的 files[0].content)複製 draggable-snap-button.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
import {
DraggableSnapButton,
type SnapPoint,
} from "@/components/ui/draggable-snap-button";
const modes: SnapPoint[] = [
{ x: -100, label: "省電" },
{ x: 0, label: "均衡" },
{ x: 100, label: "效能" },
];
<DraggableSnapButton
axis="x"
snapPoints={modes}
defaultIndex={1}
onSnapChange={(index, point) => console.log(index, point.label)}
>
拖曳
</DraggableSnapButton>不傳 snapPoints 時只有原點一個落點,就是純回彈按鈕——拖到哪、放開都彈回原位:
<DraggableSnapButton dragConstraints={boxRef} dragElastic={0.25}>
拋看看
</DraggableSnapButton>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
snapPoints | SnapPoint[] | [{ x: 0, y: 0 }] | 落點清單;放開後吸附到最近的一點。預設只有原點即純回彈 |
axis | 'both' | 'x' | 'y' | 'both' | 限制拖曳方向:自由、僅水平、僅垂直 |
index | number | — | 受控目前落點索引;提供時由外部主導位置 |
defaultIndex | number | 0 | 非受控初始落點索引 |
onSnapChange | (index: number, point: SnapPoint) => void | — | 吸附落定到「不同」落點時觸發 |
springConfig | SpringOptions | { stiffness: 520, damping: 30, mass: 0.9 } | 回彈吸附的 spring 參數 |
dragElastic | number | boolean | 0.16 | 拖出邊界時的橡皮筋彈性,需搭配 dragConstraints |
dragConstraints | RefObject | { top, left, right, bottom } | — | 拖曳邊界,透傳給 motion |
className | string | — | 覆蓋預設樣式 |
children | ReactNode | — | 按鈕內容 |
SnapPoint 為 { x?: number; y?: number; label?: string },x/y 是相對按鈕原始位置的位移(px),省略的軸視為 0;label 供輔助科技朗讀。其餘 <button> 屬性(disabled、aria-label 等)皆直接透傳。
細節
- 拖曳與吸附:以 motion 的
drag承載位置,dragMomentum關閉讓落點計算穩定;放開時只計入可拖曳的軸,找出距離最近的落點並以 spring 吸附過去 - 純回彈:
snapPoints只有原點時,任何方向拖曳放開都彈回原位;搭配dragConstraints與dragElastic可得到拖出邊界的橡皮筋手感 - 受控/非受控:提供
index進入受控模式,位置由外部主導、onSnapChange只作為請求;否則以defaultIndex起始並自行記錄落點 - 落定通知:只有吸附到「不同」於前一個的落點才會觸發
onSnapChange,回彈到原落點不重複發送 - 鍵盤支援:多落點時方向鍵(←↑ 上一段、→↓ 下一段)、
Home/End可在落點間切換,並攔下方向鍵的頁面捲動 - 無障礙:單軸多落點時補上
role="slider"與aria-valuemin/max/now/valuetext,等同滑桿語意;另以aria-describedby掛上拖曳說明、aria-live="polite"宣告目前落點 - 使用者系統開啟「減少動態效果」時:吸附仍完整發生(位置照樣移動,功能不變),僅把 spring 換成極短的線性補間,並移除拖曳時的縮放
touch-none+select-none避免觸控拖曳時觸發捲動與選字;卸載時會停掉進行中的 spring,避免動畫回呼在卸載後才觸發