← 返回目录

16. 九宫格抽奖综合实战(定时器终极应用)

把定时器、防叠加、概率控制全串起来。核心就五件事:① 8 格顺时针高亮;② 先加速后减速;③ 点击瞬间就随机定好结果;④ 权重控制中奖概率;⑤ disabled + timer 双重防连点

16.1 互动演示(完整可玩抽奖)

一等奖
二等奖
三等奖
幸运奖
谢谢参与
再来一次
红包 5 元
优惠券
点"点击抽奖":转盘先加速后减速,随机落定。抽奖中按钮禁用(防叠加)。

16.2 知识点讲解

① 整体实现思路(五步,先看懂再写码)

// 1. 摆布局:8 个奖品格子按顺时针排一圈,中间放抽奖按钮
//  都用绝对定位;⚠️ 取 div 的顺序必须跟你眼睛看到的顺时针一致
// 2. 让它转:用一个下标记现在亮着哪格,定时器一格一格切
// 3. 怎么变速:setInterval 定下来后间隔不能直接改
//  → 只能每圈"把旧闹钟砸了 → 换个速度 → 再定个新闹钟"
//  前半圈间隔越压越小(越转越快)
//  后半圈间隔越拉越大(慢慢停下来)
// 4. 怎么停:你点下去的瞬间就随机定好停哪格,算好一共走几步
//  步数走完就停(真实项目是后端先把结果告诉你,前端再停)
// 5. 防 bug:转起来时把按钮锁死(disabled + 看 timer 双保险)
//  走到终点一定要 return,不然会多走一格

② 核心代码:变量准备 + 启动函数(逐段讲)

// ====== 先把要用的家当摆好 ======
const btn = document.getElementsByClassName("button")[0]; // 中间那个抽奖按钮
const box = document.getElementsByClassName("box")[0]; // 整个九宫格的外框
// 把 8 个奖品格子全取下来,顺序正好是 HTML 里从上到下、也就是顺时针
const prizeDomList = box.getElementsByTagName("div");

let timer = null; // 闹钟编号,null 表示现在没在转(防叠加关键)
let index = 0; // 现在亮着哪一格,从第 0 格开始
let speed = 200; // 一开始每 200 毫秒亮一下
const minSpeed = 80; // 最快也不能快过 80 毫秒,封顶
let targetIndex = 0; // 最后要停在哪一格
const baseLoop = 4; // 至少要先完整转 4 圈再停
const totalNum = prizeDomList.length; // 一共 8 格

// ====== 点按钮就启动 ======
function startLottery() {
  if (timer) return; // 拦截 1:已经在转了,再点也没用,直接走人
  btn.disabled = true; // 拦截 2:把按钮按灰,不让连点
  index = 0; // 从头开始亮
  speed = 200; // 速度回初始值
  targetIndex = Math.floor(Math.random() * totalNum); // 这一刻就随机定好停哪格
  let remainStep = baseLoop * totalNum + targetIndex; // 算好总共要走几步
  // 比如 4 圈 × 8 格 + 目标格 = 正好停在中奖格
  ...
}

③ rotate 函数:高亮 + 停止 + 变速(核心中的核心)

function rotate() {
  // 1. 先把所有格子的高亮都关掉,再只亮当前这一格
  for (let i = 0; i < totalNum; i++) prizeDomList[i].classList.remove("active");
  prizeDomList[index].classList.add("active");

  // 2. 步数走完了吗?走完就该停了
  if (remainStep <= 0) {
    clearInterval(timer); // 把闹钟砸了
    timer = null;
    btn.disabled = false; // 按钮恢复可点
    alert("恭喜您获得:" + prizeDomList[index].innerHTML);
    return; // ★ 必须在这 return,不然会多走一格!
  }
  remainStep--; // 还没走完,步数减一

  // 3. 变速:前半段越跑越快,后半段慢慢减速
  if (remainStep > (baseLoop * totalNum) / 2) {
    speed -= 8; // 间隔变小 = 转得更快
    if (speed < minSpeed) speed = minSpeed; // 快到顶就别再快了
  } else {
    speed += 12; // 间隔变大 = 慢慢减速
  }

  // 4. 高亮格子往下挪一格,到第 8 格就回到第 0 格
  index++;
  if (index >= totalNum) index = 0;

  // 5. 把旧闹钟砸了,按新速度再定一个(这就是变速的写法)
  clearInterval(timer);
  timer = setInterval(rotate, speed);
}

四个坑点逐个记:① 终点判断后必须 return;② 变速 = 销毁重建定时器(间隔不能改);③ disabled + timer 双重防连点;④ 点击瞬间提前随机定结果,不停下才随机

④ 概率控制:权重数组(进阶,作业练习四)

想让"一等奖"难中、"谢谢参与"好中,就用权重:权重数字 = 这个奖在"命运转盘"上占的份数

const prizeConfig = [
  { name: "一等奖", weight: 5 }, // 一等奖只占 5 份,难中
  { name: "二等奖", weight: 15 }, // 二等奖占 15 份
  { name: "三等奖", weight: 20 }, // 三等奖占 20 份
  { name: "幸运奖", weight: 20 }, // 幸运奖占 20 份
  { name: "谢谢参与", weight: 30 }, // 谢谢参与占 30 份,最多最好中
  { name: "再来一次", weight: 5 },
  { name: "红包 5 元", weight: 3 },
  { name: "优惠券", weight: 2 }
];
// 把所有份数加起来 = 5+15+20+20+30+5+3+2 = 100
// 随机扔一个 0~99 的数,落在哪个奖的地盘上,就中哪个
// 一等奖占 [0,5)、二等奖占 [5,20)……谢谢参与占 [55,85),地盘最宽
步骤做法
1计算所有奖品权重总和
2生成 0 ~ 总权重 之间的随机数
3遍历权重数组累加,随机数落在哪个区间,对应奖品就是中奖结果

权重越大 = 区间越宽 = 越容易中。真实项目里结果由后端接口返回(防止前端作弊),前端等结果再停止转盘。

一句话记住:提前随机定结果 + 步数耗尽停止 + 销毁重建定时器变速 + disabled/timer 双防连点 + 终点 return + 权重区间控概率