canvas转盘抽奖的实现(一)

网络上已经有了很多转盘抽奖的代码,但大多是用jQuery插件实现的,其中的原理比较难弄明白,于是自己摸索了一个。最终效果如下:

 
 

实现步骤:

1.根据奖品数量绘制转盘

var      r1 = 200,    //外圆半径
         r2 = 160,    //奖品文字距离圆心的位置
         r3 = 60,    //中心按钮半径
         centerX = c.width / 2,    //中点
         centerY = c.height / 2,
         PI = Math.PI,
         prizeList = ['一等奖','二等奖','三等奖','四等奖','五等奖','六等奖','七等奖','八等奖'],    //奖品列表
         colorList = ['#ffffff','#FDFAC3','#ffffff','#FDFAC3','#ffffff','#FDFAC3','#ffffff','#FDFAC3'],    //奖品对应的背景颜色
         lenPrize = prizeList.length,
startAngle = 0, //开始绘制时的起始角度 piece
= 2 * PI / lenPrize; //根据奖品数量划分区域,单位为弧度
//绘制分区
for (var i = 0; i < lenPrize; i++) {
    ctx.fillStyle = colorList[i];
    var angle = startAngle + piece * i;
    ctx.beginPath();
    //设置圆心为起点,方便closePath()自动闭合路径
    ctx.moveTo(centerX, centerY);
    //分块绘制,目的是方便填充颜色,如果以lineTo的形式绘制,在填充颜色时会很麻烦
    ctx.arc(centerX, centerY, r1, angle, angle + piece, false);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
        
    //绘制奖品说明
    ctx.save();
    ctx.font = '30px Microsoft YaHei';
    ctx.fillStyle = '#d60000';
    ctx.translate(centerX + Math.cos(angle + piece / 2) * r2, centerY + Math.sin(angle + piece / 2) * r2);
    ctx.rotate(angle + piece / 2 + PI / 2);

    var s = prizeList[i].split('');
    for (var j = 0; j < s.length; j++) {
        var text = s[j];
        ctx.fillText(text, -ctx.measureText(text).width / 2, 32 * j);
    }
    ctx.restore();       
}            

这一部分代码的效果如下:

图一

要注意首个奖品说明的位置,也就是一等奖的位置,是从三点钟方向开始的,这是arc()方法的规定,下面这张图表示从1到N按顺序绘制。

图二

接下来绘制箭头和中心圆:

//绘制箭头
ctx.strokeStyle = '#FF5722';
ctx.fillStyle = '#FF5722';
ctx.save();
ctx.translate(centerX, centerY - 40);
ctx.moveTo( - 10, 0);
ctx.beginPath();
ctx.lineTo( - 10, 0);
ctx.lineTo( - 10, -30);
ctx.lineTo( - 20, -30);
ctx.lineTo(0, -50);
ctx.lineTo(20, -30);
ctx.lineTo(10, -30);
ctx.lineTo(10, 0);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();

//绘制中心圆
ctx.fillStyle = '#FF5722';
ctx.beginPath();
ctx.arc(centerX, centerY, r3, 0, 2 * PI, false);
ctx.closePath();
ctx.fill();
ctx.stroke();

//绘制抽奖文字
ctx.font = '30px Microsoft YaHei';
ctx.fillStyle = '#fff';
ctx.save();
ctx.translate(centerX, centerY);
ctx.fillText("抽奖", -ctx.measureText(text).width, 10);
ctx.restore();

这就是最终效果

最后实现转盘的转动,先定义几个变量:

var currentTime = 0,  //表示动画开始到现在持续的时间
    totalTime = Math.random() * 500 + 4000,   //动画总时间
    finalValue = Math.random() * 20 + 20,//动画总时间内期望的位移总量,越大转得越快,因为总时间一定,只有加快速度才能在规定时间内到达期望位移 

   t;
//setTimeout Id

转盘转动方法:

function rotation() {
    currentTime += 30;    //每帧增加30的运行时间
    if (currentTime >= totalTime) {//到达总时间后停止动画
        stopRotation();
        return;
    }
    //缓动
    var currentAngle = finalValue - easeOut(currentTime, 0, finalValue, totalTime);

    //弧度随时间递增,但增速由快变慢
    startAngle += currentAngle * PI / 180;
    
    //根据startAngle的变化重新绘制转盘,以达到转动的效果
    draw();

    t = setTimeout(rotation, 17);
}

停止转盘:

function stopRotation() {
    clearTimeout(t);
    //动画时间内转动的总弧度,因为是从三点钟方向开始绘制的,所以应当加上PI/2
    var arc = startAngle + PI / 2,
    //arc模2*PI以计算转动整圈以外不满一圈的零数
    //零数除以单位弧度,表示转动了几个单位,不满整数则向下取整(Math.floor)
    //奖品数量(以下标算,故先减1)减去转动过的单位得到当前指针所指奖品的索引
    index = lenPrize - 1 - ((arc % (2 * PI) / piece) >> 0);

    result.innerHTML = '<strong style="font-size:26px; color:#f00">' + prizeList[index] + '</strong>';
}

全部代码:

  1 < !DOCTYPE html > 
  2 <html lang = "en" > 
  3 <head > 
  4 <meta charset = "UTF-8" > 
  5 <title> Document </title>
  6  <style>
  7     body{margin: 0; font:12px Arial; background-color: #fff}
  8     .canvas_container{
  9         position: relative;
 10         width: 500px;
 11         height: 500px;
 12     }
 13     #run{
 14         position: absolute;
 15         width: 120px;
 16         height: 120px;
 17         cursor: pointer;
 18         left: 190px;
 19         top: 190px;
 20     }
 21  </style > </head>
 22  <body>
 23      <div class="canvas_container">
 24          <div id="run"></div > <canvas id = "c"width = "500"height = "500" > </canvas>
 25      </div > <div id = "result" > </div>
 26 
 27      <script>
 28      var c = document.getElementById('c'),
 29          ctx = c.getContext("2d"),
 30          r1 = 200,    / / 外圆半径r2 = 160,
 31 //文字所在位置半径
 32 r3 = 60,
 33 //中心按钮
 34 centerX = c.width / 2,
 35 //中点
 36 centerY = c.height / 2,
 37 PI = Math.PI;
 38 
 39 var prizeList = ['一等奖', '二等奖', '三等奖', '四等奖', '五等奖', '六等奖', '七等奖', '八等奖'],
 40 colorList = ['#ffffff', '#FDFAC3', '#ffffff', '#FDFAC3', '#ffffff', '#FDFAC3', '#ffffff', '#FDFAC3'],
 41 lenPrize = prizeList.length,
 42 lenColor = colorList.length,
 43 piece = 2 * PI / lenPrize,
 44 //根据奖品数量划分区域,单位弧度
 45 startAngle = 0; //起始角度
 46 function draw() {
 47     ctx.clearRect(0, 0, c.width, c.height);
 48 
 49     ctx.lineWidth = 0.5;
 50     ctx.strokeStyle = '#AF4760';
 51 
 52     //绘制分区
 53     for (var i = 0; i < lenPrize; i++) {
 54         ctx.fillStyle = colorList[i];
 55         var angle = startAngle + piece * i;
 56         ctx.beginPath();
 57         ctx.moveTo(centerX, centerY);
 58         //分块绘制,目的是方便填充颜色,如果以lineTo的形式绘制,在填充颜色时会很麻烦
 59         ctx.arc(centerX, centerY, r1, angle, angle + piece, false);
 60         ctx.closePath();
 61         ctx.fill();
 62         ctx.stroke();
 63 
 64         //绘制奖品说明
 65         ctx.save();
 66         ctx.font = '30px Microsoft YaHei';
 67         ctx.fillStyle = '#d60000';
 68         ctx.translate(centerX + Math.cos(angle + piece / 2) * r2, centerY + Math.sin(angle + piece / 2) * r2);
 69         ctx.rotate(angle + piece / 2 + PI / 2);
 70 
 71         var s = prizeList[i].split('');
 72         for (var j = 0; j < s.length; j++) {
 73             var text = s[j];
 74             ctx.fillText(text, -ctx.measureText(text).width / 2, 32 * j);
 75         }
 76         ctx.restore();
 77     }
 78 
 79     //绘制箭头
 80     ctx.strokeStyle = '#FF5722';
 81     ctx.fillStyle = '#FF5722';
 82     ctx.save();
 83     ctx.translate(centerX, centerY - 40);
 84     ctx.moveTo( - 10, 0);
 85     ctx.beginPath();
 86     ctx.lineTo( - 10, 0);
 87     ctx.lineTo( - 10, -30);
 88     ctx.lineTo( - 20, -30);
 89     ctx.lineTo(0, -50);
 90     ctx.lineTo(20, -30);
 91     ctx.lineTo(10, -30);
 92     ctx.lineTo(10, 0);
 93     ctx.closePath();
 94     ctx.fill();
 95     ctx.stroke();
 96     ctx.restore();
 97 
 98     //绘制中心圆
 99     ctx.fillStyle = '#FF5722';
100     ctx.beginPath();
101     ctx.arc(centerX, centerY, r3, 0, 2 * PI, false);
102     ctx.closePath();
103     ctx.fill();
104     ctx.stroke();
105 
106     //绘制抽奖文字
107     ctx.font = '30px Microsoft YaHei';
108     ctx.fillStyle = '#fff';
109     ctx.save();
110     ctx.translate(centerX, centerY);
111     ctx.fillText("抽奖", -ctx.measureText(text).width, 10);
112     ctx.restore();
113 
114 }
115 
116 var currentTime = 0,
117 totalTime = Math.random() * 500 + 4000,
118 finalValue = Math.random() * 20 + 20,
119 //终点值
120 t;
121 
122 function rotation() {
123     currentTime += 30;
124     if (currentTime >= totalTime) {
125         stopRotation();
126         return;
127     }
128 
129     var currentAngle = finalValue - easeOut(currentTime, 0, finalValue, totalTime);
130 
131     //弧度随时间递增,但增速由快变慢
132     startAngle += currentAngle * PI / 180;
133     draw();
134 
135     t = setTimeout(rotation, 17);
136 }
137 
138 function stopRotation() {
139     clearTimeout(t);
140 
141     var arc = startAngle + PI / 2,
142     index = lenPrize - 1 - ((arc % (2 * PI) / piece) >> 0);
143 
144     result.innerHTML = '<strong style="font-size:26px; color:#f00">' + prizeList[index] + '</strong>';
145 }
146 draw();
147 //rotation();
148 var run = document.getElementById('run'),
149 result = document.getElementById('result');
150 run.onclick = function() {
151     currentTime = 0;
152     totalTime = Math.random() * 500 + 4000;
153     finalValue = Math.random() * 20 + 20;
154     rotation();
155 };
156 
157 /*
158         t: current time(当前时间)
159         b: beginning value(初始值)
160         c: change in value(变化总量)
161         d: duration(持续时间)
162      */
163 function easeOut(t, b, c, d) {
164     return - c * (t /= d) * (t - 2) + b;
165 }
166 
167 </script>
168 </body > 
169 </html> 
View Code
原文地址:https://www.cnblogs.com/undefined000/p/canvas-turntable.html