头图
最近有一个需求要开发一个抽奖大转盘类的一个页面,本来想着这种东西肯定有现成的,但找了一圈后发现基本上都与我们的设计图有些出入,如果直接使用现成的包估计肯定是验收不通过的

开源项目的布局一般都是这样的(转盘内的文字正对圆心),推荐一个比较热门的开源项目,支持多端,多种游戏方式(大转盘、九宫格、老虎机)

image.png

而我们的设计图却是设计成这样的(文字与半径平行)

image.png

无奈现成的包的不能使用,故准备把开源项目 down 下来修改一下使之符合我们的业务场景要求

stackblitz效果预览

下面总结了一些实现思路:分为 canvas 版本和 div+css 版本

canvas 思路实现

  1. canvas 转盘绘制,包括每片扇区上的文字及图片的绘制(其实就这一点需要更改)
  2. 递归 requestAnimationFrame 然后不断改变绘制起始角度(canvas绘制的初始角度)使转盘动起来
  3. 使用缓动算法,使转盘转动更加自然:转盘初始阶段缓慢加速 -> 匀速 -> 减速 最终停在指定扇区(中奖结果肯定是 api 返回的,前端需要控制最后要停在指定位置上,按照 lucky-canvas 上的说法就是刻舟求剑)

下面分别说一下注意点:

  1. canvas 绘制需要注意,起始角度是从 三点钟开始的,绘制一圈是 2 * Math.PI,为了让起始位置落在第一个扇区上需要逆时针旋转一定的角度(按我的需求是有8个扇区,也就是需要旋转 2 * Math.PI / 8 * 2 ),开发时建议将弧度(不知道此处用弧度表示对不对😄)转换成角度,这样运算过程更加便捷,也会减少无限不循环小数产生的误差

    /**
     * 转换为运算角度
     * @param { number } deg 数学角度
     * @return { number } canvas 画圆角度
     */
    const getAngle(deg: number): number => (Math.PI / 180) * deg
  2. requestAnimationFrame 没什么好说的,现在浏览兼容性也不是什么问题了
  3. 缓动算法:其实就是根据一些列变量,算出一个值(动画的变化量),具体参考张鑫旭大大的tween.js的简单实现
  4. 转盘停止停在的扇区,根据 api 返回的中奖结果得出扇区索引 index 即:const prizeDeg = 360 - index * (360 / 8) + initDeg; (initDeg即最开始旋转的初始角度)
在需求完成之后,我发现不用 canvas,用 div 也能很方便的实现这种效果

div 实现

除了第一步转盘绘制,其他两步思路大致相同

  1. 用 div + css 代替 canvas ,主要用到了 transform: rotate(x) skew(y),需要注意的一点是,旋转扭曲之后 div 的子元素也会跟着旋转扭曲,我本来以为这样写就行了 transform: rotate(-x) skew(-y),但殊不知 transform 变换是有顺序的要这样写 transform: skew(-y) rotate(-x) 参考
  2. 用了 div 之后,旋转就不是不断地重绘 canvas 了,只需要不断地改变父集元素的 transform: rotate(),相比 canvas 要简单了许多

代码参考 https://github.com/hugeorange...

  1. 核心代码采用 ts 开发(不依赖框架)
  2. 考虑到我的这个代码满足的场景比较小众并没有封装成 npm 包
  3. 有同样场景需求的朋友可以直接拷贝代码

大桔子
588 声望51 粉丝

下一步该干什么呢...