行銷展示區塊
痛點圍攏
在輸入框打字時,周圍的痛點卡片依序向中心靠攏、越來越擁擠,送出後一起散開消失,適合在行銷頁呈現「零散工具帶來的困擾」。
行銷頁開場用的互動區塊:中央是一個真的能打字的輸入框,周圍 6–10 張痛點小卡分布在橢圓軌道上。字數越多、壓力越大,卡片依陣列順序一張張往中心靠攏、輕微傾斜縮小並彼此重疊;字數到達 pressureChars 時整群做微幅擠壓呼吸。按 Enter 或送出鈕後,卡片沿各自角度向外飛散淡出,中央換成你提供的 resolved 結果節點。開啟 autoPlay 可自動模擬逐字打字並循環播放,使用者一點輸入框就交還控制權。窄螢幕自動改為輸入框上下兩排堆疊。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add pain-point-orbitPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
12
160
編輯中的呼叫預設屬性
<PainPointOrbit/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/pain-point-orbit.json或在 components.json 設定 registries 後,改用 @webberui/pain-point-orbit 安裝。
使用
import { Mail, Sheet, CalendarClock } from "lucide-react";
import { PainPointOrbit } from "@/components/ui/pain-point-orbit";
<PainPointOrbit
points={[
{ id: "quote", text: "報價單在信箱裡找不到", icon: <Mail /> },
{ id: "sheets", text: "客戶資料散在三個試算表", icon: <Sheet /> },
{ id: "weekly", text: "每週手動彙整週報", icon: <CalendarClock /> },
]}
placeholder="今天想先處理什麼?"
autoPlay
demoText="幫我整理所有客戶的最新報價"
resolved={<div className="rounded-xl border px-4 py-3">全部集中在一個工作台</div>}
onSubmit={(value) => console.log(value)}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
points | PainPoint[] | — | 環繞輸入框的痛點卡(建議 6–10 張),依陣列順序逐張向中心靠攏 |
value | string | — | 受控輸入值;未提供時由元件內部管理(非受控) |
defaultValue | string | "" | 非受控模式的初始輸入值 |
onValueChange | (value: string) => void | — | 輸入值變動時回呼(含 autoPlay 模擬打字) |
onSubmit | (value: string) => void | — | 按 Enter 或送出鈕時回呼,參數為目前輸入值 |
onReset | () => void | — | 點「重新開始」讓卡片回到外圈時回呼 |
placeholder | string | "今天想先處理什麼?" | 輸入框佔位文字 |
label | string | "想先處理的事" | 輸入框的無障礙標籤(視覺上隱藏) |
submitLabel | string | "送出" | 送出鈕的無障礙標籤 |
resetLabel | string | "重新開始" | 重新開始按鈕文字 |
pressureChars | number | 24 | 字數達到此值時壓力為 1(所有卡片都已靠攏) |
autoPlay | boolean | false | 是否自動模擬逐字打出 demoText、送出、再重來;使用者一互動就停止 |
demoText | string | "" | autoPlay 模擬打字的內容 |
resolved | ReactNode | — | 送出後在中央淡入的結果節點;未提供時顯示預設結果卡 |
accent | string | — | 痛點卡左緣色條顏色;未提供時使用 --wb-pain-orbit-accent(預設 #f43f5e) |
stiffness | number | 160 | 卡片位移彈簧的剛度,數值越大靠攏節奏越快 |
outerRadius | number | 0.42 | 外圈半徑,佔容器寬(高)的比例 |
innerRadius | number | 0.26 | 靠攏後的內圈半徑,佔容器寬(高)的比例 |
className | string | — | 透傳到最外層容器 |
PainPoint
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別,用作 React key |
text | string | — | 一句痛點描述 |
icon | ReactNode | — | 卡片左側圖示;未提供時顯示預設的警示圖示 |
細節
- 壓力計算:
pressure = min(1, 字數 / pressureChars),字數以字元(code point)計,中文一個字算一個。第 k 張卡(從 0 起算)在pressure > k / N時啟動,往內圈移動、旋轉 ±3° 內的確定性角度、縮小到 0.92,並依啟動順序疊上 z-index。 - 卡片初始角度均分並加上以 index 算出的確定性小偏移(不使用亂數,SSR 與 client 一致);軌道半徑會夾在容器內,卡片不會超出邊界被裁切。
- 卡片位移是 spring(阻尼固定 18,
stiffness可調);送出時卡片沿各自角度飛散,每張錯開 40ms。 - 節奏 CSS 變數:
--wb-pain-orbit-scatter(散開時長,預設400ms)、--wb-pain-orbit-breathe(擁擠呼吸週期,預設1.5s)、--wb-pain-orbit-accent(左緣色條,預設#f43f5e)。在外層或全域覆寫即可。 - Enter 送出有 IME 組字保護:中文注音等輸入法組字中按 Enter 不會誤送出,並處理 Safari 在
compositionend後才發 keydown 的情況。 autoPlay會呼叫onValueChange與onSubmit;使用者聚焦、點擊或輸入時立即停止,元件離開視窗時暫停計時器。- 寬度小於 640px(Tailwind
sm斷點)時改為輸入框上下兩排堆疊。
可及性
- 輸入框以視覺隱藏的
label關聯;送出鈕為type="button"並帶aria-label,輸入為空時停用 - 痛點卡群為
role="list",每張卡為listitem;卡片不可聚焦,不搶鍵盤焦點 - 送出結果透過
aria-live="polite"區域朗讀;使用者送出後焦點移到「重新開始」,重來後回到輸入框 - 所有按鈕有
focus-visible外框 - 使用者系統開啟「減少動態效果」時,卡片不移動、不呼吸,只隨壓力改變透明度與邊框色;送出時直接淡出