繁体   English   中英

HTML5 Canvas和JavaScript奖轮

[英]HTML5 Canvas & javascript prize wheel

我在尝试使用画布实现“奖杯轮”时遇到了麻烦。 我使用的是类似于StackOverflow上浮动的画布“轮盘” http://jsfiddle.net/wYhvB/4/的东西。 我的难题是,当您单击旋转时,在后台我进行了API调用,该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();
});​

我在创建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);

然后,车轮的旋转功能循环,直到车轮的当前角度等于targetAngle。

http://www.dougtesting.net上提供了我的转盘和完整注释的源代码的工作示例。 在线示例中未启用该预定功能,但是一旦下载,可以在源代码(winwheel.js)中轻松打开该预定功能。

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

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

本页数学:缓入,缓出使用带有时间限制的Hermite曲线可以轻松消除位移 在这里,我设法全神贯注地做相同的事情-只是上/下/左/右,而不是旋转。

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

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

这是一次三次样条插值的小提琴。 http://jsfiddle.net/enhzflep/XKzGF/

这是完整的代码,因为提交向导不喜欢我的帖子。 说: “糟糕!您的答案无法提交,原因是:请通过内联相关的格式化代码使您的答案完整(不要仅仅链接到jsfiddle.net)。”

<!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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM