JS打造“九宫格抽奖”

在如今的营销活动中,抽奖功能已经成为提升用户活跃度的标配。尤其是"九宫格抽奖"这种形式,因其视觉冲击力强、交互简单、适配性好,被广泛应用于电商、社交、内容平台等各类场景。本文将带你从零开始,使用 原生 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">&times;</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() 显示结果,逻辑清晰。

四、业务层

为了防止刷奖和前端篡改,我们将"中奖逻辑"放在后端: 流程如下:

  1. 前端点击"抽奖"按钮;
  2. 请求后端接口,后端根据权重随机计算出中奖奖品;
  3. 后端返回中奖奖品的 id 和对应索引 stopIndex ;
  4. 前端根据 stopIndex 播放动画,最终高亮到对应奖品;
  5. 动画结束后弹出结果提示。

✅ 优点:

  • 前端不暴露中奖逻辑,安全性高;
  • 动画与业务解耦,可复用性强;
  • 支持运营实时调整奖品和中奖概率,无需前端上线。

后续优化建议

  1. 动画性能优化:使用 Web Animations API 或 CSS Animation 替代 setInterval ;
  2. 防抖节流:防止用户快速点击触发多次抽奖;
  3. 状态管理:引入轻量状态机(如 xstate)管理抽奖流程;
  4. 音效与震动:增强用户互动体验。
相关推荐
狂师5 分钟前
比 Playwright 更给力,推荐一个AI Agent的浏览器自动化开源项目!
前端·开源·测试
IT_陈寒14 分钟前
React hooks 闭包陷阱把我的状态吃掉了,原来问题出在这里
前端·人工智能·后端
壹方秘境15 分钟前
使用ApiCatcher在 iOS 上像修改 hosts 一样自定义域名解析
前端·后端·客户端
柳杉37 分钟前
可视化大屏设计器脚手架:从设计到交付的一站式方案
前端·three.js·数据可视化
铁皮饭盒1 小时前
3行代码搞定页面截图,Bun.WebView真的简单
javascript
kyriewen14 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒14 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
山河木马15 小时前
矩阵专题2-怎么创建视图矩阵(uViewMatrix)
javascript·webgl·计算机图形学
小林攻城狮15 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦15 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈