在如今的营销活动中,抽奖功能已经成为提升用户活跃度的标配。尤其是"九宫格抽奖"这种形式,因其视觉冲击力强、交互简单、适配性好,被广泛应用于电商、社交、内容平台等各类场景。本文将带你从零开始,使用 原生 JavaScript 实现一个可配置、可扩展、动画流畅的九宫格抽奖组件,支持运营人员通过 JSON 配置奖品信息,前端无需上线即可更新抽奖内容。

一、总体思路
1. 数据结构:奖品配置 JSON 化
为了支持运营实时配置奖品,我们将奖品信息抽象为 JSON 格式:
json
[
{"id":1,"name":"iPhone","img":"//cdn/1.png"},
{"id":2,"name":"现金50元","img":"//cdn/2.png"},
{"id":3,"name":"HUAWEI","img":"//cdn/3.png"},
{"id":4,"name":"现金10元","img":"//cdn/4.png"},
{"id":5,"name":"谢谢参与","img":"//cdn/5.png"},
{"id":6,"name":"手机优惠券","img":"//cdn/6.png"},
{"id":7,"name":"电脑优惠券","img":"//cdn/7.png"},
{"id":8,"name":"U盘","img":"//cdn/8.png"}
]
优点:前端只负责展示和动画,中奖逻辑由后端控制,避免暴露算法,提升安全性。
2.核心思路
我们将整个抽奖流程抽象为两个核心步骤:
- 绘制奖品视图:无论使用 flex 、 grid 还是 absolute ,只要按顺序渲染奖品 DOM,顺序即代表"跑道"顺序。
- 动画高亮奖品:通过setInterval控制高亮项的切换,只操作索引,不依赖具体坐标或行列。
这种设计的好处是:
- 布局可随意更换(九宫格、圆环、横向跑道);
- 动画逻辑不变,复用性极高;
- 支持动态增减奖品数量,只需更新 JSON 和 CSS。
二、html骨架
html
<div class="main">
<div class="content-container">
<div class="prize-list">
<img src="./img/prize_1.png" alt="">
<span>IphoneX</span>
</div>
<div class="prize-list">
<img src="./img/prize_2.png" alt="">
<span>现金50元</span>
</div>
<div class="prize-list">
<img src="./img/prize_3.png" alt="">
<span>HUAWEI</span>
</div>
<div class="prize-list">
<img src="./img/prize_4.png" alt="">
<span>现金10元</span>
</div>
<div class="prize-list">
<img src="./img/prize_5.png" alt="">
<span>谢谢参与</span>
</div>
<div class="prize-list">
<img src="./img/prize_6.png" alt="">
<span>手机优惠券</span>
</div>
<div class="prize-list">
<img src="./img/prize_7.png" alt="">
<span>电脑优惠券</span>
</div>
<div class="prize-list">
<img src="./img/prize_8.png" alt="">
<span>U盘</span>
</div>
<!-- 中心内容部分 -->
<div class="handler-container">
<div class="inner-container">
<img class="handler-left" src="./img/center_1.png" alt="">
<div class="handler-container-middle">
还可以抽奖 <span class="prize-number">0</span> 次
</div>
<div class="handler-container-btn"></div>
</div>
</div>
</div>
<div class="dialog-container">
<div class="dialog-main">
<div class="head">
<span class="title">温馨提示</span>
<span class="close">×</span>
</div>
<div class="content">
每次抽奖将消耗 8000 积分
</div>
<div class="dialog-main-footer">
<div class="button">再来一次</div>
</div>
</div>
</div>
</div>
三、js核心动画
js
var runGame = function () {
var random = Math.floor(Math.random() * 6000 + 3000)
timer = setInterval(function () {
random -= 200
if (random < 200) {
clearInterval(timer)
timer = null
openDialog()
return
}
currentIndex = ++index % prizeList.length
prizeList.forEach(function (node) {
node.classList.remove('active')
})
prizeList[currentIndex].classList.add('active')
}, 50)
}
技术亮点:
- 索引循环: ++index % prizeList.length 保证无限循环;
- 时间递减: random -= 200 模拟减速刹车效果;
- 动画与结果分离:动画结束后调用 openDialog() 显示结果,逻辑清晰。
四、业务层
为了防止刷奖和前端篡改,我们将"中奖逻辑"放在后端: 流程如下:
- 前端点击"抽奖"按钮;
- 请求后端接口,后端根据权重随机计算出中奖奖品;
- 后端返回中奖奖品的 id 和对应索引 stopIndex ;
- 前端根据 stopIndex 播放动画,最终高亮到对应奖品;
- 动画结束后弹出结果提示。
✅ 优点:
- 前端不暴露中奖逻辑,安全性高;
- 动画与业务解耦,可复用性强;
- 支持运营实时调整奖品和中奖概率,无需前端上线。
后续优化建议
- 动画性能优化:使用 Web Animations API 或 CSS Animation 替代 setInterval ;
- 防抖节流:防止用户快速点击触发多次抽奖;
- 状态管理:引入轻量状态机(如 xstate)管理抽奖流程;
- 音效与震动:增强用户互动体验。