网络上已经有了很多转盘抽奖的代码,但大多是用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>