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

 
 

// = totalTime) {
stopRotation();
return;
}

var currentAngle = finalValue - easeOut(currentTime,0,finalValue,totalTime);

//弧度随时间递增,但增速由快变慢
startAngle += currentAngle * PI / 180;
draw();

t = setTimeout(rotation, 17);
}

function stopRotation() {
clearTimeout(t);

var arc = startAngle + PI / 2,
index = lenPrize - 1 - ((arc % (2 * PI) / piece) >> 0);

result.innerHTML = '' + prizeList[index] + '';
}
draw();

var run = document.getElementById('run'),
result = document.getElementById('result');
run.onclick = function() {
currentTime = 0;
totalTime = Math.random() * 500 + 4000;
finalValue = Math.random() * 20 + 20;
rotation();
};

function easeOut(t,b,c,d) {
return -c *(t/=d)*(t-2) + b;
}

})();
// ]]>

实现步骤:

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

  1. var r1 = 200, //外圆半径
  2. r2 = 160, //奖品文字距离圆心的位置
  3. r3 = 60, //中心按钮半径
  4. centerX = c.width / 2, //中点
  5. centerY = c.height / 2,
  6. PI = Math.PI,
  7. prizeList = ['一等奖','二等奖','三等奖','四等奖','五等奖','六等奖','七等奖','八等奖'], //奖品列表
  8. colorList = ['#ffffff','#FDFAC3','#ffffff','#FDFAC3','#ffffff','#FDFAC3','#ffffff','#FDFAC3'], //奖品对应的背景颜色
  9. lenPrize = prizeList.length,
    startAngle = 0, //开始绘制时的起始角度
  10. piece = 2 * PI / lenPrize; //根据奖品数量划分区域,单位为弧度
  1. //绘制分区
  2. for (var i = 0; i < lenPrize; i++) {
  3. ctx.fillStyle = colorList[i];
  4. var angle = startAngle + piece * i;
  5. ctx.beginPath();
  6. //设置圆心为起点,方便closePath()自动闭合路径
  7. ctx.moveTo(centerX, centerY);
  8. //分块绘制,目的是方便填充颜色,如果以lineTo的形式绘制,在填充颜色时会很麻烦
  9. ctx.arc(centerX, centerY, r1, angle, angle + piece, false);
  10. ctx.closePath();
  11. ctx.fill();
  12. ctx.stroke();
  13.  
  14. //绘制奖品说明
  15. ctx.save();
  16. ctx.font = '30px Microsoft YaHei';
  17. ctx.fillStyle = '#d60000';
  18. ctx.translate(centerX + Math.cos(angle + piece / 2) * r2, centerY + Math.sin(angle + piece / 2) * r2);
  19. ctx.rotate(angle + piece / 2 + PI / 2);
  20.  
  21. var s = prizeList[i].split('');
  22. for (var j = 0; j < s.length; j++) {
  23. var text = s[j];
  24. ctx.fillText(text, -ctx.measureText(text).width / 2, 32 * j);
  25. }
  26. ctx.restore();
  27. }

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

图一

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

图二

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

  1. //绘制箭头
  2. ctx.strokeStyle = '#FF5722';
  3. ctx.fillStyle = '#FF5722';
  4. ctx.save();
  5. ctx.translate(centerX, centerY - 40);
  6. ctx.moveTo( - 10, 0);
  7. ctx.beginPath();
  8. ctx.lineTo( - 10, 0);
  9. ctx.lineTo( - 10, -30);
  10. ctx.lineTo( - 20, -30);
  11. ctx.lineTo(0, -50);
  12. ctx.lineTo(20, -30);
  13. ctx.lineTo(10, -30);
  14. ctx.lineTo(10, 0);
  15. ctx.closePath();
  16. ctx.fill();
  17. ctx.stroke();
  18. ctx.restore();
  19.  
  20. //绘制中心圆
  21. ctx.fillStyle = '#FF5722';
  22. ctx.beginPath();
  23. ctx.arc(centerX, centerY, r3, 0, 2 * PI, false);
  24. ctx.closePath();
  25. ctx.fill();
  26. ctx.stroke();
  27.  
  28. //绘制抽奖文字
  29. ctx.font = '30px Microsoft YaHei';
  30. ctx.fillStyle = '#fff';
  31. ctx.save();
  32. ctx.translate(centerX, centerY);
  33. ctx.fillText("抽奖", -ctx.measureText(text).width, 10);
  34. ctx.restore();

这就是最终效果

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

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

转盘转动方法:

  1. function rotation() {
  2. currentTime += 30; //每帧增加30的运行时间
  3. if (currentTime >= totalTime) {//到达总时间后停止动画
  4. stopRotation();
  5. return;
  6. }
  7. //缓动
  8. var currentAngle = finalValue - easeOut(currentTime, 0, finalValue, totalTime);
  9.  
  10. //弧度随时间递增,但增速由快变慢
  11. startAngle += currentAngle * PI / 180;
  12.  
  13. //根据startAngle的变化重新绘制转盘,以达到转动的效果
  14. draw();
  15.  
  16. t = setTimeout(rotation, 17);
  17. }

停止转盘:

  1. function stopRotation() {
  2. clearTimeout(t);
  3. //动画时间内转动的总弧度,因为是从三点钟方向开始绘制的,所以应当加上PI/2
  4. var arc = startAngle + PI / 2,
  5. //arc模2*PI以计算转动整圈以外不满一圈的零数
  6. //零数除以单位弧度,表示转动了几个单位,不满整数则向下取整(Math.floor)
  7. //奖品数量(以下标算,故先减1)减去转动过的单位得到当前指针所指奖品的索引
  8. index = lenPrize - 1 - ((arc % (2 * PI) / piece) >> 0);
  9.  
  10. result.innerHTML = '<strong style="font-size:26px; color:#f00">' + prizeList[index] + '</strong>';
  11. }

全部代码:

  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>

canvas转盘抽奖的实现(一)的更多相关文章

  1. canvas转盘抽奖的实现(二)

    本篇是<canvas转盘抽奖的实现(一)>的另一种实现方法,主要通过css3的transform以及transition过渡来实现.     // ' + r + '等奖'; } draw ...

  2. canvas转盘抽奖

    1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" ...

  3. HTML5 Canvas绘制转盘抽奖

    新项目:完整的Canvas转盘抽奖代码 https://github.com/givebest/GB-canvas-turntable 演示 http://blog.givebest.cn/GB-ca ...

  4. 转盘抽奖 canvas & 抽奖 H5 源码

    转盘抽奖 canvas https://github.com/givebest/wechat-turntalbe-canvas https://blog.givebest.cn/GB-canvas-t ...

  5. 纯CSS3大转盘抽奖(响应式、可配置)

    源于前段时候微信小程序最初火爆公测时段,把以前用 Canvas 实现的大转盘抽奖移植成微信小程序,无奈当时小程序对 Canvas 支持不够完善,只好降低用 CSS3 实现.虽然比不上 Canvas 绘 ...

  6. HTML5 Canvas圆盘抽奖应用(适用于Vue项目)

    <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...

  7. 用jQuery和PHP来实现转盘抽奖程序

    准备工作 首先要准备素材,抽奖的界面用到两张图片,圆盘图片和指针图片,实际应用中可以根据不同的需求制作不同的圆盘图片. 接着制作html页面,实例中我们在body中加入如下代码: <div cl ...

  8. javascript 转盘抽奖代码和计数器代码

    要介绍了javascript圆盘抽奖程序实现原理和完整代码例子,需要的朋友可以参考下  看到网页上有不少大转盘抽奖的应用,心血来潮也想弄个.于是找了点资料自己研究...  效果预览: 一.模拟抽奖的实 ...

  9. jquery——九宫格大转盘抽奖

    一.用到的图片 二.代码如下,重点是js部分 <!DOCTYPE html> <html> <head> <meta http-equiv="Con ...

随机推荐

  1. 洛谷P3327 [SDOI2015]约数个数和 【莫比乌斯反演】

    题目 设d(x)为x的约数个数,给定N.M,求\(\sum_{i = 1}^{N} \sum_{j = 1}^{M} d(ij)\) 输入格式 输入文件包含多组测试数据.第一行,一个整数T,表示测试数 ...

  2. vue-resource发送multipart/form-data数据

    //add headers this.$http.post('/api', data, { headers: { 'Content-Type': 'multipart/form-data' } }) ...

  3. 你能说出SQL聚集索引和非聚集索引的区别吗?

    最近突然想起前一阵和一朋友的聊天,当时他问我的问题是一个非常普通的问题:说说SQL聚集索引和非聚集索引的区别. AD:WOT2015 互联网运维与开发者大会 热销抢票 其实对于非专业的数据库操作人员来 ...

  4. CountDownLatch和CyclicBarrier 的用法

    CountDownLatch是减计数方式,计数==0时释放所有等待的线程:CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程.CountDownLatch当计数 ...

  5. usb_control_msg参数详解【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3243059.html usb_control_msg() struct usb_ctrlrequest|- ...

  6. 42深入理解C指针之---指针与队列

    一.借助第40指针与链表的相关内容,稍微修改即可: 1.定义头文件queue.h代码如下: #include <stdlib.h> #include <stdio.h> #if ...

  7. 转载 关于malloc

    1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针.如果分配失败,则返回一个空指针(NULL). 关于分配失败 ...

  8. Python和其他进程的管道通信方式--popen和popen2的比较

    目前有一个查询程序 get_user_id 是用C写的,python需要调用这个程序:使用 get_user_id  "用户名" 可以得到输出: "ID0002451&q ...

  9. js 判断变量是否为空

    js 判断变量是否为空 欢迎指正,补充! /** * 判断变量是否为空, * @param {[type]} param 变量 * @return {Boolean} 为空返回true,否则返回fal ...

  10. phpcms V9 广告模块中广告模板修改

    广告模块模板位置 \phpcms\modules\poster\install\templates\*.html 我的需求: 去掉边框控制代码,是否显示边框我将在页面模板中设置,因些需要删除模板中的以 ...