直接用 canvas 画转盘更靠谱,因 css rotate 易累积浮点误差导致指针偏移,而 canvas 每帧可精确重算角度、支持减速停稳等精细控制;抽奖需按配置概率抽,不能直接用 math.random()。

为什么直接用 canvas 画转盘比用 CSS 动画更靠谱
因为 CSS 的 transform: rotate() 在多次连续旋转时容易累积浮点误差,转几圈后指针对不准分区;而 canvas 可以每次重绘时精确计算角度、不依赖上一次状态。尤其当你要支持“点击开始→自动减速停止→回调结果”这种完整逻辑时,canvas 的控制粒度(比如每帧调整旋转速度、检测停稳阈值)远高于纯 CSS。
实操建议:
- 把转盘分成 8 个扇形区,每个占
45度,用ctx.beginPath()+ctx.arc()+ctx.lineTo()逐个绘制,别用图片贴图——否则无法动态高亮中奖区域 - 文字标签用
ctx.fillText()配合ctx.save()/ctx.restore()做局部坐标系旋转,避免手动算文本倾斜角度 - 别在
requestAnimationFrame里反复调用ctx.clearRect()清全画布——只清转盘区域(传具体矩形坐标),否则会闪屏
Math.random() 不能直接当抽奖结果用
它生成的是均匀分布的浮点数,但真实抽奖要满足「每个奖品概率可配置」,比如一等奖 5%、二等奖 15%、安慰奖 80%。直接 Math.random() 判断一等奖,看似简单,但用户连抽 10 次没中一等奖就会怀疑算法被操控。
实操建议:
- 用「概率累加数组」+「二分查找」实现:定义
prizes = [{name:'一等奖', prob:0.05}, {name:'二等奖', prob:0.15}, ...],预处理成前缀和数组[0.05, 0.20, 1.00] - 抽一次时生成
rand = Math.random(),遍历或用findIndex找第一个 ≥rand的索引 - 如果要支持「保底机制」(比如抽 10 次必中一等奖),就额外维护一个计数器,在第 10 次强制返回一等奖索引,而不是依赖随机数
转盘停不稳?关键在「减速曲线」和「停稳判定」
很多新手用线性递减角速度,导致转盘要么刹不住(冲过头),要么提前卡死(还没到目标区就停了)。真实体验要模拟物理惯性:初速度快,越接近目标越慢,最后几度几乎匀速蠕动。
实操建议:
- 目标角度设为
targetAngle(比如中奖区中心角),当前角度为currentAngle,误差diff = targetAngle - currentAngle - 角速度按
speed = Math.max(0.5, Math.abs(diff) * 0.03)动态调整——离得越近越慢,但保留最小速度防止卡住 - 停稳判定不用
diff === 0(浮点数永远不精确相等),改用Math.abs(diff) ,且连续 3 帧都满足才认为停稳 - 停稳后立刻执行回调,比如
onWin(prize),别等动画完全结束再触发——用户看到指针不动了,就该立刻反馈
手机上点不动?三个 DOM 层级陷阱
转盘按钮在 PC 上好使,一放到手机就失灵,大概率不是 JS 问题,而是触摸事件被拦截或冒泡中断。
实操建议:
- 给转盘容器加
style="touch-action: none",否则 iOS Safari 默认会截断touchstart去做页面滚动判断 - 按钮绑定
ontouchstart而不是onclick,后者在移动端有约 300ms 延迟,且某些安卓浏览器不触发 - 确保转盘 canvas 没被其他元素(比如半透明遮罩层)盖住,用浏览器调试器检查
z-index和pointer-events是否为auto
真正麻烦的是不同机型对 touchend 的触发时机差异——有些在手指抬起瞬间触发,有些等惯性滚动结束后才发。稳妥做法是:在 touchstart 里记录时间戳,touchend 时判断间隔是否
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











