HTML5 Canvas实现旋转风车与动力学转盘,关键在坐标变换、requestAnimationFrame驱动及物理模型:风车用save/translate/rotate复用叶片;转盘通过极坐标判断点击扇区,并以角速度衰减和静摩擦吸附模拟真实惯性。

用 HTML5 Canvas 制作旋转风车和动力学转盘,核心不是“画出来”,而是“动得真实”——风车要匀速轻盈,转盘要启动有力、滑行自然、停得稳准。关键在坐标变换逻辑 + 动画驱动方式 + 物理模型模拟。
风车:中心旋转 + 叶片复用绘制
风车本质是绕中心点周期性重复的叶片图形。不需要逐帧重绘整个结构,只需:
- 用 ctx.save() → ctx.translate(centerX, centerY) → ctx.rotate(angle) → 绘制单个叶片 → ctx.restore() 隔离每次旋转,避免状态污染
- 叶片可用矩形(
fillRect)或自定义路径(beginPath + lineTo),宽度建议 20–40px,长度 80–150px,颜色用渐变增强立体感 - 4 叶风车每帧 angle 增加
Math.PI / 90(即每秒约 2 圈),用requestAnimationFrame驱动,不依赖定时器
转盘:扇区划分 + 极坐标交互判断
抽奖转盘难点不在旋转,而在“用户点了哪一格”。直接用 event.offsetX/Y 容易出错,正确做法是:
- 绘制时按奖品数量均分圆周角(如 6 项奖品 → 每扇区 60° =
Math.PI / 3弧度),用arc+lineTo(center)绘制闭合扇形 - 点击时调用
canvas.getBoundingClientRect()获取真实位置,再换算为以画布中心为原点的 极坐标:dx = x - centerX; dy = y - centerY;angle = Math.atan2(dy, dx); radius = Math.hypot(dx, dy); - 判断
radius是否小于转盘半径,且angle落在哪个扇区区间内(注意atan2返回值范围是 [−π, π],需归一化到 [0, 2π))
动力学效果:角速度衰减 + 静摩擦吸附
让转盘有“惯性”和“卡位感”,不能靠固定圈数硬停。必须引入物理变量:
立即学习“前端免费学习笔记(深入)”;
- 维护两个状态量:angle(当前总旋转角度)、angularVelocity(当前角速度,单位:弧度/帧)
- 每一帧执行:
angularVelocity *= 0.972;(阻尼系数,0.96~0.985 可调手感),angle += angularVelocity; - 当
Math.abs(angularVelocity) < 0.002时,启用吸附:遍历预设的各扇区中心角(如[0, π/3, 2π/3, ...]),取最接近的一个,把angle设为该值并清零速度 - 启动时给
angularVelocity一个初始值(如 0.18),配合缓动函数(如easeOutCubic)控制减速节奏,避免“急刹”
性能与体验细节
真实项目中容易忽略但影响显著的点:
- 所有旋转操作前必须
ctx.save(),结束后ctx.restore(),否则多次rotate会累积失真 - 文字标注奖品时,用
ctx.textAlign = 'center'; ctx.textBaseline = 'middle'对齐中心,避免旋转后文字偏移 - 移动端需监听
touchstart替代click,并阻止默认行为(e.preventDefault())防止页面滚动干扰 - 可叠加轻微缩放动画(如旋转末段加
ctx.scale(0.99, 0.99)再恢复)模拟物理回弹,增强反馈



















