用 @keyframes 实现带减速的旋转动画需预设总角度与强减速缓动曲线(如 cubic-bezier(0.34,1.56,0.64,1)),配合 animation: forwards 和 animation-play-state: paused 动态停准,CSS 仅执行 JS 计算出的目标角度。

用 @keyframes 实现带减速的旋转动画,别用 linear
纯 CSS 做抽奖转盘,核心是让指针“转够圈数 + 减速停在目标位置”,不能靠 JS 控制关键帧——所以必须提前算好总旋转角度和缓动曲线。ease-out 或 cubic-bezier(0.34, 1.56, 0.64, 1) 这类强减速函数比默认 ease 更可信,linear 完全不行,停得太突兀,用户会觉得假。
常见错误是只写 rotate(3600deg) 就完事,没考虑:起始角度是否对齐、动画时长是否足够长(建议 ≥ 3s)、是否加了 transform-origin 控制旋转中心。转盘图片若没居中切图,transform-origin: center 会偏移。
- 总角度 =
360 × 圈数 + 目标扇区起始角度偏移(比如停在第3个奖品,它从 90° 开始,则补足到最近的 90° + 360×n) - 动画必须设
forwards,否则转完立刻回弹 - 别在
animation里直接写infinite,抽奖是一次性行为,要靠 JS 切换 class 触发
用 animation-play-state 暂停动画实现“手动停止”逻辑
真抽奖不可能等动画自己跑完——得在后端返回结果后,JS 计算出应停角度,再动态改 animation 的 transform 和 animation-play-state。CSS 本身不支持运行时修改 keyframes,但可以切换 class 来覆盖:
.spin {
animation: spin 4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
.spin.stopped {
animation-play-state: paused;
transform: rotate(1260deg); /* 3圈+270°,停在第4个奖品 */
}注意:直接改 style.transform 会覆盖 animation 的 transform,导致停顿卡顿;必须用 animation-play-state: paused 配合最终 transform 才能丝滑定格。
立即学习“前端免费学习笔记(深入)”;
- 移动端 Safari 对
animation-play-state支持完好(iOS ≥ 13),但老 Android WebView 可能有兼容问题,需 fallback 到 JS 动画 - 别用
transition模拟减速——它无法控制旋转圈数,精度差 - 每次抽奖前记得先移除
.stopped类,重置状态
适配移动端要注意 will-change 和硬件加速
大转盘在低端安卓机上容易掉帧,尤其带阴影或渐变边框时。单纯加 transform: translateZ(0) 不够稳定,推荐组合:
- 给转盘容器加
will-change: transform(仅在触发抽奖前加,结束后移除,避免持续占用 GPU 内存) - 确保父容器有明确宽高(% 或 rem),避免 relayout 触发重绘
- 禁用
-webkit-user-select: none,防止 iOS 上长按呼出菜单打断动画 - 如果转盘是
<img>,记得设image-rendering: -webkit-optimize-contrast防止旋转模糊
停准的关键:用 JS 算最终角度,CSS 只负责执行
CSS 做不了“根据接口返回动态停在哪”,它只是执行器。真正决定停哪儿的,是 JS 拿到后端返回的奖品索引后,结合扇区数量实时计算:
// 假设 8 个奖品,每个 45°,奖品索引 0~7 const prizeIndex = 3; const baseAngle = 45; const spinCount = 3; // 至少转 3 圈保底 const finalDeg = 360 * spinCount + (prizeIndex * baseAngle) + baseAngle / 2; // + baseAngle/2 是让指针指向扇区中心,不是边缘
这个 finalDeg 就是最终要塞进 transform: rotate(...) 的值。CSS 里不写死任何具体角度,只提供动画骨架。
最容易被忽略的是“视觉对齐”:设计稿里指针尖可能没严格对准 0°,得用 devtools 调整初始 transform 偏移量,让静止时指针正对第一个奖品起点——这一步错,后面全错。


















