系統架構摘要 (Executive Summary)
本報告統整了「智慧抽獎神器 (Lucky Ball)」的專案開發與優化脈絡。記錄了我們如何解決企業尾牙、社群抽獎中「重複中獎爭議」與「視覺枯燥」的痛點。透過純前端的「全域防重抽樣池」、Excel 批量解析矩陣,以及懸念老虎機動畫,成功打造出一款媲美專業公關公司的高顏值、高可靠性抽籤 Web App (SPA)。
每逢年底尾牙或舉辦行銷活動,主辦單位都需要進行抽獎。傳統的亂數產生器往往只能「一次抽幾個人」,缺乏「獎項分級」的概念;而專業的抽獎軟體又常需要高昂的授權費或下載安裝。
我們打造「智慧抽獎神器」的目標非常明確:做出一款「貼上名單 ➔ 設定獎項 ➔ 現場直接開獎」的極速網頁端解決方案,並且要在**公信力**與**現場視覺張力**上做到極致。
一、 拒絕黑箱與爭議:全域防重池與不放回抽樣
本章核心:機率的絕對獨立與公平性
分析為何簡易亂數工具會引發公關災難,以及我們如何透過前端狀態管理實作嚴格的「不放回抽樣 (Sampling without Replacement)」。
真實的抽獎通常是從「普獎」一路抽到「頭獎」。早期的亂數程式在分次抽獎時,如果沒有做好跨組別的狀態管理,極容易發生「某員工已經中了三獎,結果抽頭獎時又中獎」的嚴重公關災難。
架構師決策:「公平的本質,在於每一次抽樣池的精準隔離與狀態同步。」
我們在 JavaScript 前端實作了嚴格的「不放回抽樣」與「全域防重池 (Global Winners Pool)」。
當一個獎項開出後,中獎者的資料會立即被移入 winnersPool,並從候選名單中徹底剔除。
搭配我們在 智慧分組神器 中反覆驗證的 Fisher-Yates 洗牌演算法,這確保了在後續的高階獎項抽籤中,機率的獨立性與絕對的公平性,徹底杜絕重複中獎的爭議(除非使用者主動開啟「允許重複中獎」的特例開關)。
二獎 Apple Watch ➜ 3 名
圖 1.1:支援 Excel 批量解析與防重設開關的動態獎項矩陣
二、 現場氣氛的靈魂:分段開獎與懸念動畫 (Suspense UI)
本章核心:極大化視覺張力與停留時間
探討如何避免「一鍵出結果」的枯燥感,透過 requestAnimationFrame 實作老虎機式的滾動洗牌動畫,掌控尾牙現場節奏。
多數陽春抽獎程式最大的敗筆在於:點下「抽獎」按鈕的瞬間,畫面上立刻印出 10 個名字。這讓本該充滿刺激與歡呼的尾牙現場瞬間冷場,毫無氣氛與期待感可言。
我們在前端大舉翻新了視覺呈現邏輯,實作了「分段開獎儀表板」與「懸念動畫 (Suspense UI)」。
主辦人可以按照獎項層級(由小到大)獨立開獎。當點擊抽獎時,系統會調用 setInterval 與 requestAnimationFrame 啟動長達 2~3 秒的「老虎機式 (Slot Machine)」名單快速跳動特效。
這種刻意製造的視覺停頓,不僅大幅增強了活動現場的刺激感,更成功地拉長了使用者的網頁停留時間,為下方的 AdSense 廣告創造了極佳的曝光效益。
圖 2.1:結合 requestAnimationFrame 實作的老虎機懸念動畫
<!-- ========================================= -->
<!-- 模塊二:開獎儀表板 (Dashboard Section) -->
<!-- ========================================= -->
<section id="dashboard-section" class="w-full hidden fade-in flex flex-col">
<!-- 頂部工具列 -->
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-4 mb-6 flex flex-col md:flex-row justify-between items-center gap-4">
<div class="flex items-center space-x-2 sm:space-x-4 flex-wrap gap-y-2">
<button onclick="backToSetup()" class="text-slate-500 hover:text-slate-800 font-bold text-xs sm:text-sm flex items-center transition bg-slate-100 hover:bg-slate-200 px-3 py-1.5 rounded-lg">
<i class="fa-solid fa-arrow-left-long mr-2"></i> 返回修改
</button>
<!-- 💥 全局重設按鈕 -->
<button onclick="resetAllPrizes()" class="text-rose-500 hover:text-rose-700 font-bold text-xs sm:text-sm flex items-center transition bg-rose-50 hover:bg-rose-100 px-3 py-1.5 rounded-lg border border-rose-100">
<i class="fa-solid fa-rotate-left mr-1.5"></i> 重設全部
</button>
<div class="h-6 w-px bg-slate-200 hidden sm:block"></div>
<div class="text-sm font-bold text-slate-700 hidden sm:block">
總池:<span id="dash-total-pool" class="text-amber-600 mx-1 text-lg">0</span> 人
</div>
</div>
<!-- 💥 多元匯出模組 -->
<div class="flex space-x-2 flex-wrap gap-y-2 justify-center">
<button onclick="exportCSV()" class="text-xs bg-emerald-50 hover:bg-emerald-100 border border-emerald-200 text-emerald-700 font-bold py-2 px-3 rounded-lg transition-colors flex items-center gap-1.5">
<i class="fa-solid fa-file-csv text-sm"></i><span class="hidden sm:inline-block">Excel / CSV</span>
</button>
<button onclick="copyResults()" class="text-xs bg-slate-50 hover:bg-slate-100 border border-slate-200 text-slate-700 font-bold py-2 px-3 rounded-lg transition-colors flex items-center gap-1.5">
<i class="fa-regular fa-copy text-sm"></i><span class="hidden sm:inline-block">純文字</span>
</button>
<button onclick="downloadImage()" id="btnDownloadImg" class="text-xs bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 text-white font-bold py-2 px-4 rounded-xl shadow-md transition-all flex items-center gap-1.5">
<i class="fa-solid fa-camera-retro text-sm"></i><span id="btnDownloadImgText">圖卡</span>
</button>
</div>
</div>
<!-- 開獎卡片畫布區 (供截圖) -->
<div id="results-canvas" class="bg-white rounded-3xl border border-slate-200 shadow-sm p-6 sm:p-8 relative min-h-[500px]">
<div id="dashboard-cards-container" class="masonry-grid">
<!-- JS 動態生成各個獎項卡片 -->
</div>
<div id="watermark" class="hidden mt-8 text-center text-[11px] text-slate-400 border-t border-slate-100 pt-4 font-medium tracking-wider">
Generated by Gemify Lucky Ball (gemifyho.com/lucky-ball)
</div>
</div>
</section>
三、 賽後行政救星:多軌匯出與社群圖卡
dom-to-image 瀑布流截圖
與我們在 天賦探索器 中的實作類似,我們利用 dom-to-image 引擎。一鍵點擊即可隱藏操作按鈕,將動態生成的瀑布流 (Masonry) 開獎卡片轉換為高清 PNG,讓主辦方能完美且優雅地在 LINE 或 IG 限動公告得獎名單。
100% 本機端 Blob 轉換
企業抽獎往往需要進行財務核銷。我們利用 JS 的 Blob 物件,將最終的「獎項 + 姓名」對應陣列,在前端沙盒內瞬間編譯成 Excel 可讀的 CSV 檔案。整個過程不涉及任何伺服器傳輸,徹底消除了企業人資對「員工名單外洩」的資安疑慮。
四、 結語:微型工具的極致體驗
「智慧抽獎神器」完美示範了:純粹的 Math.random() 搭配 Fisher-Yates 洗牌與不放回抽樣池,就足以勝任企業級的高標準抽獎需求,達到絕對的機率對稱性。
我們將公關公司常用的複雜抽獎軟體,濃縮進一個無需安裝、開啟瀏覽器即可運作、且自帶視覺特效與圖卡匯出的 Web 應用中。這不僅解決了傳統籤筒不環保、行政負擔大的問題,更再次展現了 Gemify 體系「微型自動化與本機安全」的核心開發哲學。