zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    【线段树 树链剖分 差分 经典技巧】loj#3046. 「ZJOI2019」语言【未完】
    【图论 思维】cf715B. Complete The Graph加强
    【A* 网络流】codechef Chef and Cut
    【主席树上二分】bzoj5361: [Lydsy1805月赛]对称数
    蓝书例题之UVa 10253 Series-Parallel Networks
    HAOI2019+十二省联考 游记
    Beyas定理
    CF739E Gosha is hunting DP+wqs二分
    wqs二分
    线性规划之单纯形算法
  • 原文地址:https://www.cnblogs.com/undefined000/p/canvas-turntable.html
Copyright © 2011-2022 走看看