【问题标题】:HTML5 Canvas & javascript prize wheelHTML5 Canvas 和 javascript 奖轮
【发布时间】:2012-10-19 05:44:09
【问题描述】:

我在尝试使用画布实现“奖轮”时遇到问题。我正在使用类似于在 StackOverflow 周围浮动的画布“轮盘赌”http://jsfiddle.net/wYhvB/4/ 的东西。我的困境是,当您单击旋转时,我在后台进行了一个 API 调用,该调用返回了一个实际应该选择哪个奖品的 id,界面只不过是眼花缭乱。我将所有奖品描述推入第一个数组,如何在每个弧中添加一个 id 并在特定的弧上停止而不是在特定的随机时间停止? IE。如果 API 返回“汽车”,我希望这个轮子旋转几次并停在汽车上。

    var colors = ["##eaeaea", "##cccccc", "##eaeaea", "##cccccc",
                  "##eaeaea", "##cccccc", "##eaeaea", "##cccccc"];
    // NEED to pre load this data prior
    var prize_descriptions = ["car","house","etc..."]; // These are injected on an init call from an api
    var current_user_status = {};

    var startAngle = 0;
    var arc = Math.PI / 4;
    var spinTimeout = null;

    var spinArcStart = 10;
    var spinTime = 0;
    var spinTimeTotal = 0;

    var current_user_status = null;
    var spin_results = null;

    var ctx;

    function drawSpinnerWheel() {
      var canvas = document.getElementById("canvas");
      if (canvas.getContext) {
        var outsideRadius = 200;
        var textRadius = 160;
        var insideRadius = 125;

        ctx = canvas.getContext("2d");
        ctx.clearRect(0,0,500,500);


        ctx.strokeStyle = "black";
        ctx.lineWidth = 2;

        ctx.font = 'bold 12px Helvetica, Arial';

        for(var i = 0; i < 8; i++) {
          var angle = startAngle + i * arc;
          ctx.fillStyle = colors[i];

          ctx.beginPath();
          ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
          ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
          ctx.stroke();
          ctx.fill();

          ctx.save();
          ctx.shadowOffsetX = -1;
          ctx.shadowOffsetY = -1;
          ctx.shadowBlur    = 0;
          ctx.shadowColor   = "rgb(220,220,220)";
          ctx.fillStyle = "black";
          ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 
                        250 + Math.sin(angle + arc / 2) * textRadius);
          ctx.rotate(angle + arc / 2 + Math.PI / 2);
          var text = prize_descriptions[i];
          if (text == undefined)
            text = "Not this time! "+i;
          ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
          ctx.restore();
        } 

        //Arrow
        ctx.fillStyle = "black";
        ctx.beginPath();
        ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
        ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
        ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
        ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
        ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
        ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
        ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
        ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
        ctx.fill();
      }
    }

    function spin() {   
      spinAngleStart = Math.random() * 10 + 10;
      spinTime = 0;
      spinTimeTotal = Math.random() * 3 + 4 * 1000;
      rotateWheel();
    }

    function rotateWheel() {
      spinTime += 30;
      if(spinTime >= spinTimeTotal) {
        stopRotateWheel();
        return;
      }
      var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
      startAngle += (spinAngle * Math.PI / 180);
      drawSpinnerWheel();
      spinTimeout = setTimeout('rotateWheel()', 30);
    }

    function stopRotateWheel() {
      clearTimeout(spinTimeout);
      var degrees = startAngle * 180 / Math.PI + 90;
      var arcd = arc * 180 / Math.PI;
      var index = Math.floor((360 - degrees % 360) / arcd);
      ctx.save();
      ctx.font = 'bold 30px Helvetica, Arial';
      var text = prize_descriptions[index];
      ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10);
      ctx.restore();
    }

    function easeOut(t, b, c, d) {
      var ts = (t/=d)*t;
      var tc = ts*t;
      return b+c*(tc + -3*ts + 3*t);
    }


drawSpinnerWheel();

$("#spin").bind('click', function(e) {
  e.preventDefault();
  spin();
});​

【问题讨论】:

    标签: javascript css html animation canvas


    【解决方案1】:

    我在创建 HTML5 画布获奖轮盘方面有一些经验,我解决让轮盘停在由服务器端进程确定的特定奖品的问题的方法是定义一个奖品数组,这些奖品对应于什么显示在轮盘上,以度为单位指定每个奖品的开始和结束角度,然后在轮盘旋转之前将 targetAngle 设置为相关奖品的开始和结束角度之间的随机值加上 360 度的倍数这样轮子会旋转几次,然后在预定的奖品处减速停止。

    例如,如果轮盘有 4 个奖品,那么奖品数组是...

    var prizes = new Array();
    prizes[0] = {"name" : "Prize 1", "startAngle" : 0,   "endAngle" : 89};
    prizes[1] = {"name" : "Prize 2", "startAngle" : 90,  "endAngle" : 179};
    prizes[2] = {"name" : "Prize 3", "startAngle" : 180, "endAngle" : 269};
    prizes[3] = {"name" : "Prize 4", "startAngle" : 270, "endAngle" : 359};
    

    设置 targetAngle 的代码类似于...

    targetAngle = Math.floor(prizes[determinedPrize]['startAngle'] + (Math.random() * (prizes[determinedPrize]['endAngle'] - prizes[determinedPrize]['startAngle'])));
    targetAngle += (360 * 18);
    

    然后轮子的旋转函数循环,直到轮子的当前角度等于目标角度。

    http://www.dougtesting.net 提供了我的奖品轮盘的工作示例和完整注释的源代码。在线示例中没有启用预设功能,但下载后可以在源代码(winwheel.js)中轻松开启。

    【讨论】:

      【解决方案2】:

      我能想到的最简单的方法就是取轮盘的当前位置,然后计算这个点到奖品的距离。添加轮直径周长的随机倍数,然后你就有了一个距离。轮子的边缘必须经过这个距离才能获得奖品。

      就像您可以使用线性或三次插值以指定步数将元素从一个位置移动到另一个位置一样,您可以使用相同的方法将轮子从点 0(起点)旋转到点 1(结束点)从时间=0到时间=1

      这个页面Math: Ease In, ease Out a displacement using Hermite curve with time constraint 是一个很好的阅读。在这里,我设法绕着头做基本相同的事情 - 只是上/下/左/右,而不是旋转。

      我刚才看 iot 时有点波澜不惊。不知道是 jsfiddle、丢失的图像还是我正在运行的 25 个浏览器选项卡和程序。 无论如何,关键是使用非线性插值以指定的步数到达指定的距离。它应该在指定的时间到达那里,但不是在打开 25 个窗口的情况下.. :laughs:

      查看上面的 SO 链接。它有一些很棒的图片,可以很好地解释。

      这是当时的三次样条插值。 http://jsfiddle.net/enhzflep/XKzGF/

      这是完整的代码:

      <!DOCTYPE html>
      <html>
      <head>
      <script>
      var continuePlaying = true, isPlaying=false;
      
      function byId(a){return document.getElementById(a)}
      function myInit()
      {
      }
      window.addEventListener("load",myInit,!1);
      
      function cubicHermite(a,b,d,e,c){var g=a*a,f=g*a;return(2*f-3*g+1)*b+(f-2*g+a)*e+(-2*f+3*g)*d+(f-g)*c}
      function interp(a,b,d,e,c){var g,f;f=e/(a/2+b+d/2);g=f*a/2;f*=b;return result=c<=a?cubicHermite(c/a,0,g,0,f/b*a):c<=a+b?g+f*(c-a)/b:cubicHermite((c-a-b)/d,g+f,e,f/b*d,0)}
      function linear(a){return a}
      function cubic(a){return interp(0.35,0.3,0.35,1,a)}
      function getSize(a){return{left:a.offsetLeft,top:a.offsetTop,width:a.clientWidth,height:a.clientHeight}}
      function doAnim2(a,b,d,e){var c=a/b;setTimeout(function(){doAnimStep(0,b,c,d,e)},c)}
      function doAnimStep(a,b,d,e,c){a<=b?(setTimeout(function(){doAnimStep(a,b,d,e,c)},d),e(a/b),a++):void 0!=c&&null!=c&&c()}
      
      
      //scroll with cubic interpolation of the current scroll position
      function cubicScrollDown(b,callback)
      {
          var a=byId(b),c=a.scrollHeight-a.clientHeight;
          doAnim2(500,c,function(b){a.scrollTop=c*cubic(b)},callback);
      }
      function cubicScrollUp(b,callback)
      {
          var a=byId(b),c=a.scrollHeight-a.clientHeight;
          doAnim2(500,c,function(b){ a.scrollTop=c*(1-cubic(b)) },callback );
      }
      
      //scroll with cubic interpolation of the current scroll position
      function linearScrollDown(b, callback)
      {
          var a=byId(b),c=a.scrollHeight-a.clientHeight;
          doAnim2(500,c,function(b){a.scrollTop=c*linear(b)}, callback);
      }
      function linearScrollUp(b, callback)
      {
          var a=byId(b),c=a.scrollHeight-a.clientHeight;
          doAnim2(1000,c,function(b){ a.scrollTop=c*(1-linear(b)) }, callback );
      }
      
      function animFadeOut(elem, callback)
      {
          doAnim2(500,50,function(raw){elem.style.opacity=1-cubic(raw)},callback);
      }
      
      function animFadeIn(elem, callback)
      {
          doAnim2(500,50,function(raw){elem.style.opacity=cubic(raw)},callback);
      }
      
      
      function cubicBounce(b)
      {
          cubicScrollDown(b, downCallback);
      
          function downCallback()
          {
              cubicScrollUp(b, upCallback);
          }
      
          function upCallback()
          {
              if (continuePlaying===true)
                  setTimeout( function(){cubicBounce(b);}, 0);
              else
                  continuePlaying = true;
          }
      }
      
      function linearBounce(b)
      {
          linearScrollDown(b, downCallback);
      
          function downCallback()
          {
              linearScrollUp(b, upCallback);
          }
      
          function upCallback()
          {
              if (continuePlaying===true)
                  setTimeout( function(){linearBounce(b);}, 0);
              else
                  continuePlaying = true;
          }
      }
      
      function fadeOutIn(tgtListIdStr)
      {
          var tgt = byId(tgtListIdStr);
          
          animFadeOut(tgt,fadedOutCallback);
          
          function fadedOutCallback()
          {
              animFadeIn(tgt);
          }
      }
      
      function prependChild(parent, element)
      {
          if (parent.childNodes)
              parent.insertBefore(element, parent.childNodes[0]);
          else
              parent.appendChild(element)
      }
      
      
      
      function slideUpRemove(tgtListIdStr)
      {
          var tgt = byId(tgtListIdStr);
          var listItems = tgt.getElementsByTagName('li');
          mHeight = listItems[0].clientHeight;
          
          animFadeOut(listItems[0], slideUp);
      
          function slideUp()
          {
              doAnim2(500, 50, slideUpStep, slideUpDone);
              
              function slideUpStep(raw)
              {
                  listItems[0].style.height = (cubic(1-raw) * mHeight) + 'px';
              }
              
              function slideUpDone()
              {
                  dummy = listItems[0];
                  tgt.appendChild(dummy);
                  //dummy.removeAttribute('style');
                  dummy.style.height = null;
                  dummy.style.opacity = null;
              }
          }
      }
      
      
      
      function slideDownInsert(tgtListIdStr)
      {
          // get the container, it's items and the height of the last LI item.
          var tgt = byId(tgtListIdStr);
          var listItems = tgt.getElementsByTagName('li');
          mHeight = listItems[listItems.length-1].clientHeight;
          
          // create a dummy to take the place of the last item, set it's size and height. make it the first child of the containing list
          var dummy = document.createElement('li');
          dummy.style.opacity = 0;
          dummy.style.height = 0 + 'px';
          prependChild(tgt, dummy);
          
          // animate it!
          doAnim2(500, 50, slideDownStep,slideDownDone);
          
          function slideDownStep(raw)
          {
              dummy.style.height = (cubic(raw) * mHeight)+'px';
          }
          
          function slideDownDone()
          {
              // remove the dummy
              var newItem = listItems[listItems.length-1];
              newItem.style.opacity = 0;
              prependChild(tgt, newItem);
              tgt.removeChild(dummy);
              animFadeIn(newItem, function(){newItem.removeAttribute('style')});
          }
      }
      </script>
      <style>
      #myListDiv
      {
          width: 256px;
          padding: 6px;
          height: 128px;
          overflow-y: hidden; /*scroll;*/
          border-radius: 6px;
          border: solid 1px transparent;
          border-color: rgba(0,0,0,0.2) rgba(255,255,255,0.4) rgba(255,255,255,0.4) rgba(0,0,0,0.2);
      /*  background-image: url(img/rss128.png);  */
          background-color: rgba(0,0,0,0.1);
      }
      h4, p
      {
          margin: 6px 0;
      }
      
      ul 
      {
          padding: 0;
          list-style: none;
          margin: 0;
      }
      
      ul#mList li
      {
          padding: 0 8px;
          margin: 0 6px;
          display: block;
          border: solid 1px #cccccc;
          border-bottom-color: #000;
          border-color: #ccc transparent #000 transparent;
          vertical-align: middle;
          background-color: rgba(150,150,150,0.95);
          overflow: hidden;
      }
      .thumb
      {
          width: 48px;
          height: 48px;
          float: left; 
      }
      .thumb img
      {
          height: 48px;
      }
      #mPanel
      {
          display: inline-block;
          float: left;
          padding: 32px;
          background-color: hsl(80,50%,20%);
      }
      </style>
      </head>
      <body>
          <div id='mPanel'>
              <div id='myListDiv'>
                  <ul id='mList'>
                      <li><div class='thumb'><img src='img/opera.svg'></div><div class='itemData'><h4><a>Item #1</a></h4><p>some assorted text</p></div></li>
                      <li><div class='thumb'><img src='img/chromeEyes.svg'></div><h4><a>Item #2</a></h4><p>some assorted text</p></li>
                      <li><div class='thumb'><img src='img/girl.png'></div><h4><a>Item #3</a></h4><p>some assorted text</p></li>          
                      <li><div class='thumb'><img src='img/chuck-norris.jpg'></div><h4><a>Item #1</a></h4><p>some assorted text</p></li>          
                      <li><div class='thumb'><img src='img/redBaron.jpg'></div><h4><a>Item #2</a></h4><p>some assorted text</p></li>
                      <li><div class='thumb'><img src='img/default.png'></div><h4><a>Item #3</a></h4><p>some assorted text</p></li>
                  </ul>
              </div>
          </div>
          
          <button onclick='cubicScrollDown("myListDiv")'>Cubic down</button>
          <button onclick='cubicScrollUp("myListDiv")'>Cubic up</button><br>
          <button onclick='cubicBounce("myListDiv")'>cubic bounce</button>
          <button onclick='linearBounce("myListDiv")'>linear bounce</button><br>
          
          <input type='button' onclick='slideUpRemove("mList")' value='newest'/>
          <input type='button' onclick='slideDownInsert("mList")' value='Slide Down'/><br>
          <button onclick='continuePlaying=false'>Stop Anim cycle</button>
          <input type='button' onclick='fadeOutIn("mList");' value='fadeOutIn'/><br>
      </body>
      </html>
      

      【讨论】:

        猜你喜欢
        • 2011-07-21
        • 2012-07-24
        • 2015-08-09
        • 2011-05-11
        • 1970-01-01
        • 2013-09-19
        • 2013-10-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多