美文网首页
canvas实现转盘抽奖

canvas实现转盘抽奖

作者: 我爱吃花牛 | 来源:发表于2017-03-23 20:07 被阅读0次

    用canvas写了一个简单的转盘抽奖插件,

    给大家参考下下。。。


    做的时候的想法是,通过传进来的标签以及属性,直接可以new出一个转盘

    如:var a = new createRound(obj,{...}) ;

    因为是用canvas来写,所以肯定会用到角度,弧度了什么的,所以做好准备工作:

    1,角度转弧度 

    说明:因为2π弧度等于360度,所以通过这个封装成以下函数

    function d2a(n){

           return n * Math.PI / 180;

    };

    2,n - m 区间获取随机数,一会后面会用到

    function rnd(n,m){

          return parseInt(Math.random()*(m-n))+n;

    };

    3,因为要重复使用,所以想着用面向对象,现在编辑构造函数。根据我们开始定义的使用方法来传值:

    说明:

    传进obj后,我们在obj里面添加创建一个canvas,并且给他赋值宽高,(canvas宽度以及高度,必须用属性来写,写在样式里会进行拉伸哦),所以我直接写的是 this.oC.width = this.config.width ; 来定义属性值;

    this.init() 里面放的时直接调用的一些函数,之所以都放到this.config 的 json 里,是因为这样更灵活,不用担心顺序而出现错误,里面有一些在绘制时需要用到的值 和一些默认值 ;

    4,this.init() 里的内容

    说明:因为如果没有抽奖信息的话,转盘是不成立的,所以做了初始判断,this.config.data ,如果没有数据就 return 出去;

    5,画弧,也可以说是画圆

    说明:

    this.gd.save() : 保存画布坐标系统的状态

    this.gd.beginPath() : 重新画(清除之前的路径)

    this.gd.closePath() : 闭合路径(自动连接到起始点)

    this.gd.restore() : 恢复save之后设置的状态

    可以简单理解为调用restore之后,restore方法前调用的rotate/translate/scale方法全部就还原了,画布的坐标系统恢复到save方法之前,但是这里要注意的是,restore方法的调用只影响restore之后绘制的内容,对restore之前已经绘制到屏幕上的图形不会产生任何影响。(这段话是从网上找的,我自己总结不了这么清楚。。。) ;

    接下来要开始绘制圆了

    this.gd.moveTo( 起点x , 起点y ) ; 通过这连接其他地方

    this.gd.arc(圆心x,圆心y,半径,起始角度,结束角度)  ,因为里面需要 的是弧度,所以我们刚才封装的d2a(n) 用到了。

    this.gd.fillStyle = 填充颜色;

    如上图所示,我们将它封装好,因为一会转盘内部分块的时候还需要用到;

    我们要根据传进来的 data 来确定分成多少块,所以在上面 第 4 步的时候,我们把数据传进去调用,

    c 是数据 传进来的颜色 。

    a 是起始角度,从 0度 开始 减去偏移量,因为画弧度是默认开始角度 是90度(如图1所示),所以需要减90(得到图2) 想让第一块内容在中心位置(如图3),所以需要减去当前块的一半角度 360 / arr.length / 2,找了个变量将要减的值存储下来 this.config.pyl 。

    b 是结束角度,值是(i + 1),也同样需要减去偏移量。

    数据下新增 start 和 end 将起始角度和结束角度存储下来 ,  因为这个需要填写的是正确的角度 ,所以需要将刚才减去的 90度 加上,后面的 90/arr.length 是在随机区域内的角度的时候 ,不到于太靠边上。

    调用刚才封装好的 drow 函数,并将值 传进去 循环创建多块内容

    图1 图2  图3

    6,绘制指针

    说明:画箭头,用数组存储位置,这个箭头稍微有点长,不过如果圆盘小的时候可以根据需要进行适当调整

    this.gd.moveTo() : 起始位置定义为中心靠左 5 像素 ;

    循环数组 连接 里点的点 ,接着做了一个阴影的处理,设置了箭头的颜色 ;

    找了半天没有找到怎么移动自己划出来的图形的方法,因为图形是由点连成线绘制,所以也没法使用 translate 或者 rotate ,所以将它转换成图片,然后对图片进行操作,当图片加载完成后,调用 _this.clearOther(0) 函数,下面会对该函数说明。

    7,为指针转动做准备工作

    说明:当指针旋转的时候必须清空画布,要不然会连续绘制,达不到我们想要的效果,所以里面的好多内容需要重新绘制,上面对代码有备注,大家可以看一下

    旋转箭头时,rotate,默认以画布左上角为支点,箭头生成的图片与画布大小一样,我们要以画布的中心对箭头进行旋转,所以我们需要先将箭头 移到 中心位置,translate 后,与刚才不同的是 支点 移动到中心位置,但是整体位置已经偏移,所以在 rotate 后 我们需要将 图片自身 的位置 定义为 负值,让图片回到自己最初的位置

    8,指针转动到指定位置

    说明:因为一张图无法截取完整代码,所以分两张图。

    因为是当用户触发时开启定时器,所以为了防止用户在连续开启定时器,所以,首先定义一个开关,当用户点击时 判断 是否在进行,如果在 进行,就 return 出来,如果没有进行,定义值,现在开始进行,当运动结束后,改变值,记录运动已经结束。

    因为当将方法是在用户触发时调用,所以当用户调用时需要将中奖id传参进去,循环数据,将每个数据索引存储起来,进行判断,如果当前索引下数据的 id 等于 传参进来的 id 则将该中奖索引记录下来,然后获取到数据该索引下的内容,将之前存储的 起始角度(start)和 结束角度 (end ) 取出来,通过随机数,找到其区域内的角度 为 目标角度(t)。

    n 为当前进行的角度 ,默认快速旋转3圈后,开始慢慢减速,所以当 n > 360*3 ( 3 圈分界线 ) 的角度后,开始慢慢减速,(可能大家有疑问,为什么要给 t 加上 360 ,是因为如果 t 等于 10度 或者特别小的度数的时候,由慢变慢的过程不明显,所以又加了360,让它有个慢慢减速的过程)。当进行的角度 减去 分割线后 大于 等于 目标角度(t)后,证明已经到达角度终点,停止定时器,将进行角度 归 0。

    用户点击时调用该方法

    9,绘制奖品信息,展示数据提供的文字信息,进行封装

    this.gd.textBaseline = ' top ' ;  字体位置,如下图所示:

    this.gd.textAlign = ' center ' ;   如下图所示:

    this.gd.translate(this.config.cx,this.config.cy); 将字体默认定义在画布中心,本来的算用 rotate 来处理文字的角度,但是用了之后,文字位置角度是正确了,可是文字本身角度也进行了调整,所以没办法,只好通过文字自身的 x , y 来调整所处的正确位置。

    var x = Math.sin(d2a(i*360/l)) * (this.config.r - 50);   因为角度和斜线的位置我们是已知的(因为我们定义所有文字据中心的位置都是相等的 相当于半径 ,所以我们知道斜线位置,角度就是 360 除以 份数后 乘以 i 得出来的值),然后通过下图公式去求出 x 值,y 值也是同样方法。(学好数理化,走遍天下都不怕,骄傲脸 ^_^ )

    this.gd.fillText(str,x,-y); 将设置好的值传进去,就可以定义好文字的位置了,因为 y 值 的 0 位置在画布中心,所以当位置在上面时自然就成了负值 (大家不要用 x轴y轴的想法去想哦)。

    10,循环数据,给数据中的不同文字定义不同位置

    说明:循环数据,使用之前封装好的方法将数据下的每一份的文字定义位置。

    11,封装成功,开始调用

    说明:这个是自己弄的假数据 ,大家可根据后台提供的不同数据来对代码进行,适当的调整


    写到这,转盘抽奖就差不多完了,第一次总结自己写的小demo,有不对的地方欢迎大家多多指教,在写的时候 是按照 自己封装好后的代码顺序 一块一块进行的,大家看着可能有些乱,可以去 github 上下载代码进行参考,看起来会更直观一点。

    地址:https://github.com/juanjuanGit/rotary-draw.git

    相关文章

      网友评论

          本文标题:canvas实现转盘抽奖

          本文链接:https://www.haomeiwen.com/subject/tektottx.html