十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用纯前端打造可审计的年会抽奖工具:从名单到动画的完整实现

用纯前端打造可审计的年会抽奖工具:从名单到动画的完整实现 每年年会季我都会被同事拉去当“场外技术支援”问题永远逃不开那句“抽奖环节怎么做才不尬”早几年我用公司OA里的抽奖模块背景假、动画卡、名单里偶尔出现乱码中奖名单刷新慢台下甚至有人起哄说“这是内定”。后来我干脆自己写了一个纯前端的网页版抽奖工具也就是朋友们口中说的抽奖神器一套页面跑了好几年年会从几十人的部门聚餐到几百人的公司年会都验证过稳定、公平、可审计最关键的是现场效果足够“燃”。这篇文章不是丢一份现成源码就完事我会把年会抽奖真实需要的功能、设计思路、核心代码、现场踩坑全部拆开讲。包括名单怎么批量导入、如何保证多次抽奖绝不重复、大屏滚动怎么实现既不卡又有仪式感、遇到乱码和兼容性问题怎么收场。无论你是行政策划还是前端新人照着我这个方案改成自己的基本不会翻车。1. 先想清楚年会抽奖到底要解决什么问题1.1 需求表面是“抽个人”实际是控场、公平、不出错很多人以为抽奖工具就是随机挑一个人其实真正到了年会上需求比想象中多得多。我第一年代码写得太简单结果现场遇到过这些情况三等奖抽完名单里居然有人第二次中奖抽奖时大屏背景图片加载不出来黑屏了三秒钟主持人喊“停”的时机和程序停止时机对不上气氛掉一半。所以你去找策划、行政聊一圈会发现真正被反复要求的是这几件事第一操作要简单主持人或者行政后台点一下就开始点一下才停止不要让不懂技术的人研究半天第二奖项必须分等级特等奖、一二三等奖以及阳光普照奖每轮抽多少人要能预先配置第三一个人只能中一次高级别奖项不能抽到前面已经中过的人第四现场要有名单滚动、声音反馈最好有个“开始-减速-停”的过程让观众情绪能调动起来第五中奖结果要有记录结束后方便行政确认和发奖。当需求被这样拆开后技术方案自然清晰了。核心不是造一个多炫酷的算法而是做一个现场可控、逻辑严谨、出现异常能快速兜底的工具。很多人把关注点放在“随机”两个字上其实放在第一位的是流程闭环名单管理、奖池去重、结果记录这些细节才是年检现场真正会出问题的地方。1.2 为什么我用纯前端网页方案而不是 App 或小程序技术选型上我见过有人用微信群抽奖小程序但年会大屏抽奖需要现场弹名单、播放音效小程序在后台权限和投屏体验上很受限。也有人用 Python 写命令行抽奖自己能玩但主持人不会用。我最终选定了“纯前端网页 本地运行”的方案一个index.html负责结构、style.css负责视觉、script.js负责逻辑名单放在一个独立的 JSON 文件里。整个工具不需要服务器不需要装依赖只要能打开 Chrome 或 Edge 的电脑都能跑双击index.html就能开始。这个方案还有一个隐形优势名单数据都在本地不经过第三方平台年会前最敏感的资料不外泄这件事自然解决了。画面上为了让大屏效果好我会做成 16:9 的全屏模式背景用 CSS 渐变和动画去模拟舞台灯光不再依赖网图。音效文件可以本地放一首短短的鼓点或提示音避免现场没网导致素材加载失败。你可能会觉得纯前端是不是不够专业实际上大型活动后台也会有备用本地方案年会上表现最稳的往往就是越少外部依赖的工具。1.3 模块怎么划分才不容易乱为了避免写成一个几百行代码挤在一起的脚本我通常把整个工具拆成几个独立模块名单配置、奖项配置、抽奖核心逻辑、滚动动画、展示UI、记录存储。这几块之间只通过几个公共函数交互。名单配置负责解析人员数据处理重名问题给每个人一个唯一ID。奖项配置以数组形式维护奖项名称、本轮人数、顺序。抽奖核心逻辑维护“全体人员池”和“已中奖人员集合”保证抽取时自动剔除。滚动动画用定时器或requestAnimationFrame实现名字快速切换形成滚动视觉效果。展示UI负责把人员显示在大屏中央中奖结果弹窗展示。记录存储每次抽奖后把结果写入localStorage同时追加一份可复制的文本记录。实际开发中尽量把“抽谁出来”和“怎么展示出来”分开。分开之后后续哪怕你想换转盘模式、刮刮乐模式也只改展示层不用动核心规则。我第一版混在一起写后发现有活动方想改成“不同奖项不同滚动风格”只能重写一半代码重构后就灵活多了。2. 功能设计与核心模块拆解2.1 页面布局大屏中央是主角控制区要低调年会现场通常是一台电脑连接投影仪或LED大屏所以页面视觉需要为“远距离观看”设计。我的做法是页面整体只分两个视觉层次。第一层是舞台背景占满全屏可放“XX公司2024年度盛典”标题、年份、装饰光束第二层是抽奖展示区位于屏幕中央偏上此处显示当前滚动姓名、中奖名单和奖项名称。控制按钮我通常放在页面右下角颜色做暗一点或者半透明避免被台下观众看清操作。主持人侧也可以另配一台笔记本电脑通过网络访问本机页面做“遥控”但更稳妥的是直接在同一台大屏电脑上用无线鼠标控制。有人会把控制区和展示区做成两个页面用 WebSocket 通信以实现“站在后台偷偷控制”的体验。这个方案我也试过复杂度直接上升现场还多一个故障点如果不是几百人以上大场面没必要。在适配性上优先保证 1920x1080 分辨率下的效果。小分辨率的电脑我也做了兼容处理核心区域用vw和vh布局字体大小用clamp()限制边界中奖名单多时能自动缩小字号这样即使是竖屏或 1366x768 的老笔记本页面也不会溢出。2.2 名单和奖项的数据结构决定了后续好不好改一次年会活动数据我用这样的 JSON 结构来维护。名单部分每条数据至少包含id、name、department。这里id不推荐使用数组下标因为抽奖过程中要从池子里移出人员用下标容易混乱。最直接的办法是使用“工号”或“姓名-部门”这种唯一组合作为 id避免各部门存在同名同姓的人被当成一个人。奖项配置我用数组维护因为年会抽奖顺序基本固定先阳光普照、再三等奖、二等奖、一等奖、特等奖所以数组顺序就代表抽奖顺序。每个奖项包含三个字段奖项名称、抽取人数、奖项展示文案。有人可能觉得中奖人数放到配置里就行但我还会额外加一个“抽奖顺序”字段防止有人调整奖项顺序后数组乱掉。示例配置大致是这样{ title: XX科技2025迎新晚会, awards: [ { id: welfare, name: 阳光普照奖, count: 50, order: 1 }, { id: third, name: 三等奖, count: 8, order: 2 }, { id: second, name: 二等奖, count: 3, order: 3 }, { id: first, name: 一等奖, count: 1, order: 4 } ] }名单部分如果人数太多不建议手写在 HTML 里。我通常单独维护一个names.json文件用fetch去读取。但直接双击 HTML 文件时浏览器对fetch本地文件有限制会出现跨域问题。所以最简单省事的方式是把名单以 JSON 字符串嵌进 HTML 的一个script typeapplication/json标签里这样既能结构化又不会因为打开方式而报错后期替换名单也只需要改一个区块的内容。2.3 抽奖规则随机但绝不能重复才是核心逻辑所有活动方都会强调一个底线同一人不能重复中奖。这个逻辑如果在每次抽取时临时判断容易漏更好的做法是维护一个“奖池池子”每次抽完立刻把中奖者从池子里移走。公式很简单const remainingPool allPersons.filter(person !wonIdSet.has(person.id));这里有几个细节必须注意。第一不同奖项如果采用“不同人员范围参加”比如“特等奖仅限经理级以上抽”这种特殊需求需要在池子里预先过滤人员。我一般建议把这种特殊规则提前和策划沟通清楚在代码中写一个isEligible(person, awardId)的判断函数免得现场才改规则。第二每轮抽取数量如果大于剩余池子数量必须弹窗提示并且禁止开抽例如总人数只有 20 人三等奖却要抽 25 人这种配置错误很容易在彩排时暴露。随机算法本身很多人会直接写Math.random()然后排序我早年也这么干const winners [...pool].sort(() Math.random() - 0.5).slice(0, targetCount);这段代码在人数很少的时候有概率分布不均匀的问题虽然影响不大但既然要把它当作品做就要靠谱一点。推荐用 Fisher-Yates 洗牌算法从池子末端取人这样每次抽取的概率是真正均匀的。核心实现如下function fisherYatesShuffle(arr) { const result [...arr]; for (let i result.length - 1; i 0; i--) { const j Math.floor(Math.random() * (i 1)); [result[i], result[j]] [result[j], result[i]]; } return result; } function getWinners(pool, count) { const shuffled fisherYatesShuffle(pool); return shuffled.slice(0, count); }抽完一轮后用forEach把每个人的 id 塞进wonIdSet同时将姓名保存到该奖项的 winners 数组。这张“已中奖”清单还会输出到页面底部方便现场有疑问时随时回查。3. 手写实现一个可落地的大屏抽奖页3.1 搭建基础页面结构和舞台样式这里我给出一个精简但可直接跑通的核心模板。页面顶部显示活动标题和当前奖项中间区域是滚动展示区右下角是操作按钮。不用任何框架保证任何电脑打开都没障碍。HTML 骨架大致是这个样子!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / title年会抽奖大屏/title link relstylesheet hrefstyle.css / /head body main idapp div classstage-bg/div header classpage-header h1 idpageTitleXX公司2025年会盛典/h1 p classaward-label当前奖项span idawardName三等奖/span/p /header section classdraw-area div idrollerBox classroller-box !-- 滚动过程中每次将一个候选人卡片渲染到这里 -- /div div idwinnersPanel classwinners-panel hidden !-- 中奖结果展示 -- /div /section footer classcontrol-bar select idawardSelect/select button iddrawBtn classprimary-btn开始抽奖/button button idresetBtn classghost-btn重置本次活动/button textarea idlogOutput readonly/textarea /footer /main script srcscript.js/script /body /html样式上最重要的一点是让舞台背景铺满屏幕并且最外层禁止用户误选中文字、误触滚动。对应 CSS 里的几个关键设置body设为overflow: hidden和height: 100vh选手名字或其他文字用user-select: none。背景我不用背景图而是一层深蓝渐变加几个半透明圆形光斑然后用 CSS 动画让光斑缓慢移动这种“灯光感”成本很低但现场氛围不错。中奖结果展示区默认隐藏当一轮抽奖结束后会把本次中奖人员姓名卡片排列出来。默认一次中奖人数少时姓名大且居中人数多时通过display: flex; flex-wrap: wrap; justify-content: center自动折行每个姓名变成一个小卡片像荣誉墙一样依次飞入。这里的关键技巧是避免在展示结果时清空全部 DOM 再重建那样容易闪屏更好的做法是先准备好卡片节点然后逐个添加show类用 CSS 过渡实现错峰入场。3.2 滚动动画与“开始即悬念停止即揭晓”的实现抽奖现场最容易被注意到的细节其实不是中奖名单本身而是名字滚动到停止那一瞬间的节奏。我实现的是按下“开始抽奖”后大屏中央的名字以很快的间隔随机切换营造出“好多人在闪”的错觉按下停止后并不会瞬间停死而是有一个延迟越来越长的减速过程最后落定到中奖者这种体验和转盘最终滑过的感觉一致观众会更兴奋。具体做法是维护一组intervalMs它从初始的 50 毫秒开始在停止流程里每切换一次后增加 40-60 毫秒切换 8 到 12 次后完全停止。这比直接clearInterval再显示结果要自然得多。我用setTimeout链来控制递减节奏而不是用setInterval因为每一轮的延迟时间都在变直接在 setTimeout 里修改下一个 tick 更清晰。核心代码可以这样写let rolling false; let rollTimer null; function tickRoll() { // 从剩余池里随机挑一个人显示 const pool getRemainingPool(); const randomOne pool[Math.floor(Math.random() * pool.length)]; renderRollingCard(randomOne); } function startRolling() { rolling true; rollTimer setInterval(tickRoll, 60); } function stopRolling(finalWinner) { if (!rolling) return; rolling false; clearInterval(rollTimer); // 减速展示先继续跳动间隔逐渐变大 let delays []; let base 60; for (let i 0; i 10; i) { base 50 i * 20; delays.push(base); } let step 0; function slowDownTick() { if (step delays.length) { // 每次都随机换一个非最终候选的名字制造悬念 renderRollingCard(pickRandomExcept(finalWinner.id)); step; setTimeout(slowDownTick, delays[step - 1]); } else { renderRollingCard(finalWinner); showWinner(finalWinner); } } setTimeout(slowDownTick, 50); }这里需要提醒一个细节由于停止按钮按下后还要滚动约一两秒所以业务上要先把真正的中奖者确定下来再在减速动画里做“假滚动”否则动画结束后再去调随机算法会因为池子已经变化而导致展示和结果对不上。很多新手写抽奖工具问题恰恰出在这里把“最终结果的确定”和“动画结束”耦合得太晚现场一旦手抖点了两下就会出现一个展示人员和最终记录不一致的尴尬情况。另一个现场常见需求是“是否可以滚动过程中再次按键停止”。我建议把开始和停止做成同一个按钮状态机分为待抽奖、滚动中、已停止三个阶段。按钮文案随着状态切换待抽奖时显示“开始抽奖”滚动中显示“停止抽奖”结果展示后显示“下一轮”。这样鼠标只需要点一个按钮新手也能很快掌握。按钮的重复点击事件要加锁防止异步动画还没结束又触发了下一轮。3.3 名单批量导入Excel 数据怎么快速进到页面如果公司上百人人工在 HTML 里加名单显然太蠢。我通常是这样做的行政拿到 Excel 名单后只保留“姓名、部门”两列然后把表格另存为 CSV 文件。如果你直接用 Excel 打开 CSV再复制到别处大概率会遇到中文乱码。正确做法是把 CSV 文件用 VSCode 或 Notepad 打开确认右下角编码是 UTF-8再全选粘贴到在线转换工具或者本地脚本中。考虑到年会现场断网我自己习惯在本地用一段小逻辑处理。其实还有一种更省事的方式让名单生成一段 JSON 嵌入到页面里。用 Excel 公式也能做到但稍微麻烦我推荐直接使用文本编辑器的替换功能。例如把 CSV 里两列之间用逗号分隔张三,产品部 李四,研发部先将“,”替换为,department:再把每行首尾补上 JSON 结构很快能拼出[ { name: 张三, department: 产品部 }, { name: 李四, department: 研发部 } ]要预防乱码最保险的是把最终这份 JSON 数据放到 HTML 文件里保存时用 VSCode 选择“UTF-8 with BOM”或直接以 UTF-8 保存。只要meta charsetUTF-8 /和文件本身编码一致导入名单基本不会出问题。这个细节我在后面问题排查里还会再强调因为它真的是每年都能遇到的高发坑。名单中如果出现重名我的建议是以“姓名-部门”作为页面展示后缀例如“张三产品部”。但读取人员唯一标识时依然不要用展示文本内部始终使用一个隐藏的 id 属性。我在导入名单时会给每个人自动补充一个从 1 开始的序号 id然后在控制台中打印总人数方便彩排时核对名单是不是完整。3.4 音效、弹窗和氛围细节决定了“高级感”而不是“学生作品”音效是年会抽奖里容易被忽略但实际影响很大的点。第一次使用时我直接在按钮点击里调用audio.play()结果现场没声音后来查了才知道浏览器为了防骚扰默认不允许网页自动播放音频。解决方式是在用户第一次点击“开始抽奖”按钮时顺带调用一次audio.play()然后在 Promise 的then中把音频对象保留后续再播放就不会被拦截。更稳的做法是在页面加载后先显示“点击页面任意位置启动音效系统”这样的轻量引导点完再正式启用按钮。页面在停止瞬间还可以加一个简单但有效的视觉冲击突然弹出中奖名单卡片给每张卡片加一个scale动画从 0.3 放大到 1同时加上一个小阴影。如果不想引入粒子库也可以自己用 CSS 写“彩带碎片”在宣布中奖的瞬间向页面容器中添加 20-30 个颜色随机的小方块指定 CSS 动画让它们从顶部掉落。因为碎片数量不多浏览器压力很小却能明显提升现场氛围。我会在抽奖页面的左上角显示一个“剩余可抽人数”让主持人知道当前奖池还剩下多少人。每抽完一轮这个数字会变化。这个小数字看似简单实际操作中非常有价值因为很多行政人员主持时会紧张看到剩余人数就能快速判断配置的奖是否够抽不会出现抽到一半才发现人员不够的尴尬情况。4. 实际会场里的那些坑一份问题速查表4.1 中文乱码、重复导入、名单全没了根源多半在编码年会中最常见的问题就是名单乱码。乱码现象通常是两个成因一个是 HTML 文件本身不是 UTF-8 编码另一个是名单文件被 Excel 打开并另存为 GBK 后又被浏览器按 UTF-8 读取。前者好解决在 VSCode 右下角点编码选择“通过编码保存”改成 UTF-8 即可。后者要特别留意千万别在 Excel 里直接打开 UTF-8 的 CSV 再保存Excel 会默认把它转成 GBK。如果你确实只是临时复制了几十个名字粘贴进页面但页面显示一堆“锟斤拷”说明文件编码在保存时已经发生了变化。这时最直接的修复是把自己写的英文加名单的结构全部清空重新用 VSCode 新建一个 UTF-8 文件再粘入名单。我自己还在名单 JSON 尾部加过一段自检脚本会在加载后自动检查里面是否存在乱码常用字符比如“锟斤拷”“烫烫烫”一旦发现就在控制台抛警告。这个功能虽然只花了几行代码但彩排时能节省大量时间。还有一种名单相关的问题是“导入重复”。行政发来的 Excel 里可能有人因为调动部门出现了两行导致同一人重复存在于名单中。我在代码里默认不自动去重而是打印重复项让人工决定是否剔除。因为自动去重虽然省事但也可能误删同名不同人的记录。彩排时我会先看控制台输出的“重复候选人提示”如果有疑似重复再回到 Excel 里去核对工号。4.2 电脑太旧、大屏分辨率不匹配画面卡顿或模糊怎么办年会现场的电脑配置往往不高尤其很多公司会拿一台多年没用的“古董笔记本”接 LED 大屏。这种情况下如果 JS 每秒更新太多画面CPU 占用会拉满导致滚动出现卡帧。所以针对低配电脑我做了两件事一是滚动过程不用requestAnimationFrame去无脑刷默认用 50-60 毫秒一次的setInterval切名字视觉效果上人眼已经足够感觉到频闪却不给 CPU 增加额外负担二是所有动画尽量只操作transform和opacity避免页面每次切换都触发大范围重排。大屏分辨率不匹配也很常见。很多 LED 屏分的分辨率不是标准 1920x1080可能是 1536x768 或更奇怪的拼接分辨率。如果还按固定像素设计页面会出现画面被拉伸或边缘被裁切。我的方案是把舞台做成 16:9 的固定逻辑宽高然后通过 CSS 的transform: scale()自动适配任意屏幕。也就是说页面内部始终按照 1920x1080 设计加载外层时用脚本计算当前窗口宽高比再把整个 app 容器等比缩放居中。缩放的代码很简单但有一个坑如果直接对整个body缩放鼠标定位也会被缩放影响按钮点击区域可能偏移。我的处理是缩放只套一层专门负责视觉效果的外壳真实的按钮和控制栏放在这个视觉层外面控制栏的定位也单独按窗口左下角来。这样即使缩放比例不是 1:1按钮点击依然是准确的。4.3 彩排没做够上台最容易翻车的几个点我每年代年会前都会反复催活动负责人“必须找一个和现场同型号、同分辨率的显示环境试一遍。”很多团队只在一台 27 寸显示器上点点看觉得没问题结果一到 LED 大屏才发现字体太小、背景刺眼。专门针对大屏调试的目标是最后一排的人能不能看清中奖人名字。如果看不清要么把字体调大要么单次只展示一两个名字。彩排时还需要确认主持人的停止口令和电脑操作同步。人不是机器主持人口中喊“停”和现场操作点击停止之间总有零点几秒延迟这没问题但提前要约定是“看到大屏名字开始变慢再入场喊下一句”还是“提前三秒把手放到鼠标上”。我还在按钮旁加了一个轻提示每次停止前程序会在滚动 8 次之后进入减速状态所以主持人可以看着减速阶段说一些串词等最终名字落定时刚好接入下一次欢呼。没有经历过这种彩排的主持人容易因为节奏错位而冷场。另一次现场事故让我记忆深刻行政小姐姐在连点按钮时不小心点到了浏览器的刷新键结果页面回到初始化状态。当时我设计了“中奖记录保存在 localStorage”功能刷新后还能恢复名单和奖项进度。所以你们如果要抄作业一定不要漏掉这个存储逻辑。重置操作我放在页面角落并且会弹确认框防止误触。4.4 现场有人质疑随机性透明化审计是最好的回应有人说抽奖程序“看起来是随机的谁知道是不是后台作弊”。年会本来图个乐但如果老板运气爆棚抽中特等奖台下总有起哄的。这种时候技术解释没有用最有用的办法是把过程透明化。我在每次抽奖结束后会把本轮中奖人员、时间、所在奖项都追加写入页面的一个日志面板中。活动结束时可以一键复制整个中奖记录现场投到大屏给所有人看。更进一步我还会在抽取特等奖这样的大奖前先在大屏上展示剩余候选人数量让主持人当众喊一个“我将从剩余 N 名未中奖同事中抽出特等奖”这样每个人心里都有预期中奖人不会和之前的获奖名单重叠。如果想连“每次点击随机”本身也无可指摘可以改用浏览器提供的加密级随机源crypto.getRandomValues()来生成随机索引至少从技术上比起Math.random()更严谨。不过说实话现场氛围到了那一步几乎没人要求你用密码学随机只要名单不重复、展示合理、结果能复查就已经超出绝大多数抽奖工具的水平了。我把现场排查经验整理成一个速查表方便大家在年会前对照检查问题现象可能原因快速处理思路中文字符显示为乱码文件编码不是 UTF-8用 VSCode 打开后另存为 UTF-8名单导入后有人重复中奖两个不同记录被当成一个人给每条数据加唯一 id而不是只比姓名现场刷新页面后状态丢失未使用本地存储保存进度抽奖即时写入 localStorage并提供恢复按钮声音点击后没反应浏览器自动播放限制首次点击按钮时主动初始化音频大屏显示模糊页面固定像素未按实际分辨率适配用 scale 方式等比缩放整套视觉层LED 屏上画面被裁切分辨率比例不是 16:9用缩放容器 预置黑边方案5. 从“能用”到“好用”再进阶一点的经验5.1 打通签到与会后导出一次活动数据的完整链路年会抽奖如果只是现场抽完就散价值略低。很多公司还希望把中奖名单和签到数据对上方便后续统计到场率、阳光普照奖的发放范围。如果你不打算开发完整签到系统一个低配但实用的方案是邀请函或签到时用问卷工具收集参会人员名单导出 Excel再把同一份名单用于抽奖页。抽完奖后把程序记录的获奖名单和签到表做一次“姓名 部门”的匹配这样既能确认到场人员是否全部参与抽奖又能在发奖时避免有人代领冒领。我从第二年就开始在抽奖页里增加“导出中奖 CSV”按钮。它的原理其实很简单遍历所有奖项的 winners 数组把每个人员对象转成 CSV 格式的一行再通过 Blob 下载到本地。有了这个按钮行政不用手动敲中奖名单发奖和对账都高效很多。对非技术同事来说他们不会关心你怎么实现只关心“这个文件能直接进 Excel 吗”所以导出编码必须设置成带 UTF-8 BOM否则 Excel 打开还是乱码。5.2 抽奖玩法的扩展摇一摇、口令红包、滚动抽座位抽奖工具做成熟之后很容易接到新的活动需求。比如有人想把“领导随机抽”改为“全员摇一摇”希望手机和电脑端互动。这种玩法本质上是把同一个大屏抽奖页面当成服务器参与者手机扫码后上报事件大屏做图形化排序是另一种技术栈了。除非有必要我并不建议年会上临时接入需要高并发支撑的玩法尤其供应商网络不稳定时摇一摇很容易演变成大型翻车现场。相对稳妥的扩展是“座位号抽奖”和“隐藏口令”玩法。座位号抽奖本质和人员抽奖一样只是把名单换成座位号结果按座位反查人员。隐藏口令则可以由主持人在开奖前给出一个大家会一起喊的词比如“龙年大吉”然后大家通过手机扫码输入口令参与。这种玩法比分房间安静坐着要有趣但对网络要求较高需要活动场地提供稳定 WiFi。小型团建可以简单点直接用网页版的座位号和人员双模式切换就足够了。我建议在做扩展前先把“主流程稳定性”放第一位。如果你不是专门做活动开发不要追求太多花活。曾经有一次我试图把刮刮乐、赛马转盘、猜拳 PK 全部塞进一个工具结果开发时间拉长了一倍最后彩排时反而因为某些动画库加载问题差点误事。后来我把同一套抽奖内核保留将玩法做成皮肤前端切换 CSS 和对应动画核心逻辑只字未动才真正兼顾了现场稳定和新颖感。5.3 代码层面以后还能怎么优化如果你想把这份代码变成一个长期可维护的小工具我建议后续加入单元测试。抽奖最核心的逻辑无非是“从池子里无重复抽取 N 人”这个逻辑完全可以写成一个纯函数做测试。我自己的习惯是准备一份 20 人的测试名单反复抽 2000 轮检查每一轮是否有重复、每次人数是否正确、所有轮次结束后是否所有人最终都有均等机会。不要在年会前夕临时去测日常开发时就把这些回环测试写完能发现很多隐藏 bug。另外就是维护一份友好的README.txt里面说明怎么改名单、怎么配置奖项名称和人数、怎么一键重置。这个文件虽然不进代码逻辑但能帮你明年接手时省下大量回忆时间。行政同事换了一轮后只要照着说明做一个新名单就能继续用这套工具。我见过太多临时的活动代码半年后本人回看都看不懂变量名是什么意思更不用说让别人维护了。代码里每个函数名尽量语义化重要步骤写中文注释但注释要解释“为什么”而不是照着代码念一遍。最后再分享一点个人经验做年会抽奖工具这几年我最大的体会是技术上难的不是弹出随机结果而是把所有边界情况想清楚。名单有没有空数量够不够抽按钮被连点怎么办有人中途退出屏幕怎么办刷新之后怎么恢复这些都处理干净程序才算稳定。现场我还会准备一个备用的“纯文字模式”万一大屏动画卡死用最简单的列表形式把中奖名单打印出来主持人照样可以完成流程。这个兜底方案一次都没用到但每年我都会保留。如果你正准备做自己公司的年会抽奖页别急着去网上找一堆华而不实的模板把名单管理、去重抽人、结果记录这三个基本功做扎实再考虑视觉和音效现场就稳了一大半。后面哪怕有人想改成刮刮乐、座位抽、摇一摇核心抽奖模型依旧不用改。希望这篇复盘能帮你避掉那些我踩过的坑让年会抽奖真正成为全场最开心的环节。
返回列表