作为一名前端开发工程师,每年这个时候都会接到年会抽奖系统的开发需求。今年我决定把这个过程记录下来,分享一个基于浏览器的轻量级抽奖解决方案。这个方案最大的特点就是无需后端服务,所有数据都存储在浏览器端,真正做到开箱即用。
这个抽奖程序主要包含以下几个核心模块:
程序采用纯前端技术栈实现,数据存储在IndexedDB中,这意味着:
为什么选择这样的技术方案?主要基于以下考虑:
提示:如果预计参与抽奖人数超过5000人,建议改用WebSQL或考虑后端方案,因为IndexedDB在大数据量下性能会下降。
javascript复制// 奖项数据示例结构
const prizes = [
{ name: "特等奖", count: 1, color: "#FF0000" },
{ name: "一等奖", count: 3, color: "#FF9900" },
// ...其他奖项
]
内定规则设置:
背景自定义:
数据初始化技巧:
程序使用IndexedDB作为主要存储引擎,相比localStorage有以下优势:
| 特性 | IndexedDB | localStorage |
|---|---|---|
| 容量 | ≥250MB | 5-10MB |
| 数据类型 | 结构化数据 | 仅字符串 |
| 查询能力 | 支持索引查询 | 全量遍历 |
| 事务支持 | 有 | 无 |
关键实现代码:
javascript复制// 初始化数据库
const dbRequest = indexedDB.open('LotteryDB', 1);
dbRequest.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('participants')) {
const store = db.createObjectStore('participants', { keyPath: 'id' });
store.createIndex('name', 'name', { unique: false });
store.createIndex('awarded', 'awarded', { unique: false });
}
// 类似创建其他表...
};
抽奖核心逻辑采用Fisher-Yates洗牌算法,保证公平性:
javascript复制function drawWinner(prizeId) {
return new Promise((resolve) => {
// 获取所有未中奖人员
const tx = db.transaction('participants', 'readonly');
const store = tx.objectStore('participants');
const index = store.index('awarded');
const request = index.getAll(false);
request.onsuccess = () => {
const candidates = request.result;
// 检查内定规则
const fixedWinner = checkFixedRules(prizeId, candidates);
if (fixedWinner) {
resolve(fixedWinner);
return;
}
// 随机选择
const winner = candidates[Math.floor(Math.random() * candidates.length)];
resolve(winner);
};
});
}
问题1:名单导入失败
问题2:抽奖结果未保存
问题3:页面刷新后设置丢失
彩排很重要:
数据备份:
性能优化:
现场应急方案:
通过修改CSS可以实现多种视觉效果:
css复制/* 修改中奖人显示样式 */
.winner-display {
font-size: 5rem;
text-shadow: 0 0 10px gold;
animation: glow 1s infinite alternate;
}
@keyframes glow {
from { text-shadow: 0 0 5px #fff; }
to { text-shadow: 0 0 20px gold, 0 0 10px orange; }
}
多屏互动:
数据统计:
互动增强:
这个抽奖系统我已经在实际年会中使用过三次,每次都会根据反馈进行迭代。最大的体会是:看似简单的抽奖功能,在实际现场环境中会面临各种意外情况,所以一定要做好充分的测试和应急预案。