WebberUI

擲筊

點擊擲出兩枚筊杯的 3D 翻轉動畫,落地判定聖杯/笑杯/陰杯並可綁 onResult 做趣味決策

廟宇文化的趣味互動:一張漸層供桌上放著兩枚紅色新月形筊杯(純 CSS,一面平、一面凸),閒置時微幅呼吸;點供桌或按 Enter/空白鍵,兩枚各自沿隨機軌跡拋起、多圈翻滾、落地小彈跳並揚起塵埃,落定後依「一平一凸=聖杯、兩平=笑杯、兩凸=陰杯」判定,以大字加短語顯示結果。可開啟「連續三聖杯才算」模式、注入固定種子的亂數、調整三種結果的權重,並透過 onResult 取得含歷史的完整結果——適合抽獎、決策小工具與節慶活動頁。

載入預覽⋯
npx shadcn@latest add https://webberui.com/r/moon-blocks-toss.json

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

72
<MoonBlocksToss />

安裝

npx shadcn@latest add https://webberui.com/r/moon-blocks-toss.json

或在 components.json 設定 registries 後,改用 @webberui/moon-blocks-toss 安裝。

使用

import { MoonBlocksToss } from "@/components/ui/moon-blocks-toss";

<MoonBlocksToss
  questionText="今天要不要吃鹹酥雞?"
  buttonText="擲筊問問看"
  requireThree
  onResult={(result, history) => {
    console.log(`第 ${result.index} 擲:${result.outcome}`, result.faces);
    if (result.confirmed) console.log("三聖杯定案!", history);
  }}
/>

想讓結果可重現(例如寫測試或做每日固定籤),把 rng 換成有種子的產生器;weights 則可以調整三種結果的相對機率(娛樂用途,預設即兩枚公平筊杯的理論比例 2:1:1):

import {
  MoonBlocksToss,
  pickMoonBlocksOutcome,
  resolveMoonBlocks,
} from "@/components/ui/moon-blocks-toss";

// 簡單的線性同餘產生器:同一個種子每次都得到同樣的序列
function seeded(seed: number) {
  let s = seed >>> 0;
  return () => {
    s = (s * 1664525 + 1013904223) >>> 0;
    return s / 4294967296;
  };
}

<MoonBlocksToss rng={seeded(20260818)} weights={{ sheng: 3, xiao: 1, yin: 1 }} />;

// 判定與抽樣函式可單獨使用
resolveMoonBlocks("flat", "round"); // "sheng"
pickMoonBlocksOutcome(Math.random); // "sheng" | "xiao" | "yin"

Props

Prop型別預設值說明
valueMoonBlocksResult | null受控:目前顯示的擲筊結果,null 為尚未擲出;不提供時為非受控模式
defaultValueMoonBlocksResult | nullnull非受控模式的初始結果
onChange(result: MoonBlocksResult | null) => void結果變動時回呼:落定為新結果,按「重來」時為 null
onResult(result: MoonBlocksResult, history: MoonBlocksResult[]) => void每次落定時回呼,附上含本次在內的完整歷史(由舊到新)
labelsPartial<MoonBlocksLabels>見下表自訂結果名稱與短語(未給的欄位沿用預設)
requireThreebooleanfalse連續三聖杯才算數:顯示 3 個進度圓點,第三個聖杯落定時 result.confirmedtrue
questionTextstring顯示在供桌上方的問題文字
buttonTextstring"擲筊"供桌下方擲筊按鈕的文字
sizenumber72單枚筊杯的寬度(px),供桌高度與拋高依比例縮放
weightsMoonBlocksWeights{ sheng: 2, xiao: 1, yin: 1 }三種結果的相對權重(娛樂用途;預設為兩枚公平筊杯的理論比例 2:1:1)
rng() => numberMath.random亂數來源,需回傳 [0, 1) 區間的數字;可注入固定種子的產生器讓結果可重現
disabledbooleanfalse停用擲筊
classNamestring透傳到最外層容器

MoonBlocksResult

欄位型別預設值說明
outcome"sheng" | "xiao" | "yin"判定結果:聖杯/笑杯/陰杯
faces["flat" | "round", "flat" | "round"]兩枚筊杯各自朝上的面(左、右);flat 平面、round 凸面
indexnumber本次是第幾擲(自 1 起算,按「重來」後歸零)
streaknumberrequireThree 模式下截至本次的連續聖杯數(0–3;非該模式恆為 0)
confirmedbooleanrequireThree 模式下本次是否湊滿三連聖杯

MoonBlocksLabels

欄位型別預設值說明
shengstring"聖杯"聖杯名稱
xiaostring"笑杯"笑杯名稱
yinstring"陰杯"陰杯名稱
shengHintstring"允杯——同意,可以放心去做"聖杯短語
xiaoHintstring"笑而不答——問法再想想,再擲一次"笑杯短語
yinHintstring"不允——這次先別,換個方式問"陰杯短語
idlestring"點桌面或按下方按鈕擲出"尚未擲出時的提示
tossingstring"筊杯落地中……"筊杯落地前的提示
streakstring"連續聖杯"requireThree 模式下進度圓點旁的說明
confirmedstring"三聖杯!定案"requireThree 模式下湊滿三聖杯的短語
resetstring"重來"重來按鈕文字

MoonBlocksWeights

欄位型別預設值說明
shengnumber2聖杯的相對權重
xiaonumber1笑杯的相對權重
yinnumber1陰杯的相對權重

權重只看相對比例,全為 0 或負數時會退回預設比例。另外具名匯出 resolveMoonBlocks(left, right)(由兩面判定結果)、pickMoonBlocksOutcome(rng, weights?)(依權重抽樣)、DEFAULT_MOON_BLOCKS_LABELS,以及 MoonBlocksOutcomeMoonBlockFace 型別,方便在外層重用同一套規則。

細節

  • 判定規則:每枚筊杯一面平(陽面,較亮)、一面凸(陰面,較暗有隆起陰影)。一平一凸為聖杯(允杯)、兩平為笑杯(笑而不答)、兩凸為陰杯(不允);兩枚公平投擲的理論比例是 2:1:1,也就是預設 weights
  • 先抽結果再排面:亂數只在點擊事件裡取用(render 期間不碰 Math.random,避免水合不一致),先依權重抽出結果,再反推兩枚的面——聖杯時隨機決定哪一枚是平面,畫面不會每次都一樣
  • 翻滾角度只增不減:每次拋擲在上一次落定的 rotateXrotateY 上再累加 2–4 圈與 1–2 圈(方向隨機),朝上的面靠 0°/180° 的半圈偏移決定,因此 motion 會從當前角度接著轉,不會先倒轉回零;兩面各自 backface-visibility: hidden 背對背貼合,露出哪一面完全由角度決定
  • 拋物線與落地y 以五個關鍵影格走「起拋 → 最高點 → 落地 → 小彈跳 → 靜止」,翻滾在落地時刻停止;桌面投影同步縮小變淡再恢復,塵埃小點延遲到落地瞬間才向兩側散開。兩枚各自的動畫結束後才寫入結果並觸發 onResult,不用計時器,卸載也不需清理
  • 供桌即按鈕:整塊供桌就是原生 button,點桌面、按鈕視覺或用鍵盤都能擲,拋擲中重複觸發會被忽略
  • 連續三聖杯requireThree 開啟時,每個聖杯讓 streak 加一、其他結果歸零,第三個聖杯把 confirmed 設為 true 並換成定案短語;下一擲會重新從零累計
  • 受控模式:傳入 value 後畫面跟隨父層給的結果(含兩枚的面),按「重來」時 onChange 收到 null

可及性

  • 整張供桌是單一原生 buttontype="button"),Enter/空白鍵即可擲出;aria-label 帶入按鈕文字與上一擲的結果,aria-busy 標示拋擲中,aria-describedby 連到結果區;筊杯、投影與塵埃皆為裝飾並以 aria-hidden 隱藏
  • 結果區為 role="status"aria-live="polite",落定後螢幕閱讀器會朗讀結果名稱與短語;requireThree 的進度圓點以 role="img"aria-label 朗讀「連續聖杯 n / 3」
  • 供桌與重來按鈕皆有 focus-visible 焦點環,disabled 時降低不透明度並停用互動
  • 使用者系統開啟「減少動態效果」時,停用翻滾、拋物線、塵埃與閒置呼吸,按下即直接落定並顯示結果

本頁目錄