直播購物疊層(React)
直播賣場 UI 疊層的 React 元件:固定比例舞台上疊 LIVE 徽章與觀看人數滾動、愛心噴發、彈幕留言流、「+1」喊單自動偵測與浮出計數、置頂商品卡與庫存倒數,可放 children 當真正的影片。
這是 WebberUI Pro 元件
上線活動期間免費:註冊或登入後,在上方預覽區按「複製安裝指令」就能直接安裝,不需付費、不用綁信用卡。下方那條指令未登入時會回 401。
台灣社群直播、電商直播的「喊單」文化:主播介紹商品,觀眾在留言區打「+1」就代表下單。這個元件把整套直播賣場的 UI 疊層封裝成一個固定比例的舞台(直式 9:16 或橫式 16:9)——底層是漸層+緩慢漂移的光暈當作影片占位(也可放 children 當真正的 video),上面疊:左上 LIVE 徽章與逐位滾動的觀看人數、右側愛心按鈕與上飄粒子、左下留言流(新訊息從底部滑入、舊的上移淡出、帳號以色塊首字當頭像)、+1 自動偵測(符合規則的留言高亮,商品卡上方浮出「+1」字樣,訂單數與庫存即時更新)、置頂商品卡與「+1 喊單」按鈕。留言串支援受控/非受控,也可以開 simulate 讓元件自己模擬留言與觀看人數變動;所有帳號、留言與商品皆為虛構示範資料。
載入預覽⋯
npx shadcn@latest add "https://webberui.com/r/live-shopping-overlay.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
5
<LiveShoppingOverlay />
安裝
npx shadcn@latest add "https://webberui.com/r/live-shopping-overlay.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/live-shopping-overlay 安裝。
使用
import {
LiveShoppingOverlay,
isPlusOne,
} from "@/components/ui/live-shopping-overlay";
// 示範模式:留言流與觀看人數由元件內部模擬(商品與帳號皆為虛構)
<LiveShoppingOverlay
simulate
product={{ name: "陶瓷手沖濾杯組", price: 680, stock: 12 }}
onPlusOne={(msg) => console.log("喊單", msg.user, msg.text)}
onHeart={(count) => console.log("愛心", count)}
/>
// 正式接後端:留言受控、觀看人數由 API 提供,舞台底層放真正的 video
<LiveShoppingOverlay
layout="landscape"
messages={messages}
onMessagesChange={setMessages}
viewers={viewers}
product={product}
onPlusOne={(msg) => createOrder(msg)}
>
<video src={streamUrl} autoPlay muted playsInline />
</LiveShoppingOverlay>
// 喊單判定函式可單獨使用(例如在後端或表單層過濾留言)
isPlusOne("+1 黑色"); // true
isPlusOne("我要 1 個"); // true
isPlusOne("有優惠碼嗎"); // falseProps
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
messages | LiveMessage[] | — | 受控留言串(由舊到新);提供時內部狀態與 simulate 失效,新增留言請透過 onMessagesChange 回寫 |
defaultMessages | LiveMessage[] | [] | 非受控模式的初始留言 |
onMessagesChange | (messages: LiveMessage[]) => void | — | 留言串變動時回呼(按「+1 喊單」或模擬新增留言);受控模式下由此更新 messages |
simulate | boolean | false | 非受控時由元件內部以計時器模擬留言流(含隨機 +1);示範用,正式接後端時請關閉 |
product | LiveProduct | 內建虛構商品(NT$680、庫存 20) | 置頂商品(名稱、售價、起始庫存) |
viewers | number | — | 受控觀看人數;未提供時由內部模擬每隔數秒微幅浮動 |
defaultViewers | number | 1280 | 內部模擬觀看人數的起始值(viewers 未提供時才有效) |
showViewers | boolean | true | 是否顯示右上角觀看人數 |
title | string | "週三晚上開賣" | 直播間標題,顯示於 LIVE 徽章右側 |
selfName | string | "我" | 按「+1 喊單」時送出留言所用的帳號名稱 |
onPlusOne | (message: LiveMessage) => void | — | 每偵測到一則新的 +1 留言時回呼一次(含自己按下的喊單) |
onHeart | (count: number) => void | — | 點擊愛心時回呼,帶入累計次數 |
layout | "portrait" | "landscape" | "portrait" | 舞台比例:直式 9:16(手機直播)或橫式 16:9 |
maxMessages | number | 5 | 留言流最多同時顯示的則數,超過的舊留言上移淡出 |
children | React.ReactNode | — | 放在舞台底層的節點(例如真正的 video),會被拉伸填滿並置於疊層之下 |
className | string | — | 透傳到最外層舞台容器 |
LiveMessage
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別碼;元件以此判斷「新訊息」,同一個 id 不會重複觸發 +1 |
user | string | — | 發言帳號名稱(頭像取首字,並依名稱雜湊上色) |
text | string | — | 留言內容;符合 +1 規則時自動高亮並計入喊單 |
ts | number | — | 送出時間(毫秒時間戳),選填、僅供呼叫端排序或顯示 |
LiveProduct
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
name | string | — | 商品名稱 |
price | number | — | 售價(新台幣) |
stock | number | — | 起始庫存;每一筆 +1 扣一件,扣到 0 顯示完售並停用喊單按鈕 |
另外具名匯出 isPlusOne(text) 與 LiveShoppingLayout 型別,方便在後端或表單層重複使用同一套喊單判定規則。
細節
- +1 判定規則(
isPlusOne):留言 trim 後符合/(^|\s)\+?1(\s|$)/(獨立的「1」或「+1」,例如「1」「我要 1 個」),或內文任何位置含「+1」/全形「+1」(例如「+1黑色」「幫我留一個+1」) - 訂單數與庫存:每一則「新的」+1 留言扣一件庫存並觸發
onPlusOne;元件以id記錄已處理的留言,同一個id不會重複計數。初始留言(defaultMessages或首次傳入的messages)中的 +1 直接算進起始訂單數,但不觸發回呼與浮出動畫,SSR 與客戶端輸出一致 - 受控/非受控:提供
messages即為受控,元件不再自己新增留言(simulate失效),按「+1 喊單」會以onMessagesChange([...messages, 新留言])回寫,由你決定是否送到後端。非受控時內部保留的則數比maxMessages多一些,動態調大上限還有舊留言可補 - 模擬留言流:以
setTimeout鏈而非固定setInterval排程,每則間隔帶確定性抖動,讀起來像真人留言而非節拍器;約三成五機率喊單,完售後只出一般聊天;分頁切到背景時不塞留言,避免回來瞬間一大串動畫同時播放;卸載時清理計時器 - 觀看人數:
viewers受控或以defaultViewers起始由內部每 3 秒微幅浮動(約 ±3%),數字逐位滾動;千分位格式自行實作,不走toLocaleString,避免伺服器與瀏覽器 ICU 差異造成 hydration 不一致 - 愛心與「+1」浮出粒子皆以 motion 自行實作,每顆的飄移、擺幅、縮放與色調由序號推導的確定性偽亂數算出,render 期間不呼叫
Math.random;粒子壽命結束即移除並清理計時器 - 底層「影片」占位是漸層+兩顆緩慢漂移的光暈,不用外部影片/圖片;傳入
children時會被拉伸填滿(object-cover)並置於所有疊層之下,上下漸暗遮罩確保白字、留言與商品卡在任何底圖上都可讀 - 庫存倒數:商品卡進度條隨每筆 +1 縮短,剩 3 件以內轉紅並顯示「最後 N 件」,扣到 0 顯示「已完售」並停用喊單按鈕
可及性
- 最外層為
role="region"並以aria-label帶入直播間標題;愛心按鈕與「+1 喊單」皆為type="button",具focus-visible環,完售時喊單按鈕disabled - 留言流為
role="log"但aria-live="off"——模擬留言每一兩秒一則,逐則播報會淹沒螢幕閱讀器;只有自己按下「+1 喊單」時,才透過隱形的role="status"區域播報累計單數與剩餘庫存 - 觀看人數不放
aria-live(每隔數秒就變),改以sr-only文字補上「人觀看中」單位;愛心按鈕的aria-label帶入已送出次數 - 所有裝飾性元素(光暈、漸暗遮罩、頭像色塊、粒子、進度條)皆
aria-hidden,留言文字本身仍是可讀的 DOM 內容 - 使用者系統開啟「減少動態效果」時,停用光暈漂移、LIVE 呼吸點、愛心與「+1」粒子、數字滾動與留言滑入動畫,只保留數字、高亮與文字更新;模擬留言與觀看人數變動的間隔放慢一倍