0

キャンバスを使用して「賞品ホイール」を実装しようとすると問題が発生します。 StackOverflow に浮かんでいるキャンバスの「ルーレット ホイール」 http://jsfiddle.net/wYhvB/4/に似たものを使用しています。私のジレンマは、スピンをクリックしたときです。バックグラウンドで、どの賞品が実際に選択されるべきかを示す ID を返す API 呼び出しを行います。インターフェイスは単なる見栄えです。すべての賞品の説明を最初の配列にプッシュしています。どのように各アークに ID を追加し、特定のランダムな時間で停止するのではなく、特定のアークで停止できますか? IE API が "car" を返した場合、このホイールを数回回転させて car で停止させます。

    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();
});​
4

2 に答える 2

4

私は 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) で簡単に有効にすることができます。

于 2013-07-18T10:14:59.990 に答える
1

私が考える最も簡単な方法は、ホイールの現在の位置を取得し、このポイントから賞品までの距離を計算することです。車輪の直径の円周の倍数の乱数を追加すると、距離が得られます。ホイールの端は、この距離を移動して最終的に賞品を獲得する必要があります。

線形補間または 3 次補間を使用して要素を 1 つの位置から別の位置に指定されたステップ数で移動できるように、同じ方法を使用してホイールをポイント 0 (開始点) からポイント 1 (終了点) まで回転させることができます。 time=0 から time=1

このページ数学: エルミート曲線を時間制約付きで使用して変位をイーズ イン、イーズ アウトするのは良い読み物です。回転ではなく、上下左右だけです。

ちょうど今 iot を見ている間、それは少し途切れ途切れです。それがjsfiddleなのか、欠落している画像なのか、それとも私が実行している25個のブラウザタブとプログラムなのかわかりません。とにかく、ポイントは、非線形補間を使用して、指定されたステップ数で指定された距離に到達することです。指定された時間内にそこに到着するはずですが、25個のウィンドウが開いているとは限りません.. :笑い:

上記のSOリンクをチェックしてください。本当によく説明する素晴らしい写真がいくつかあります。

これは、当面の 3 次スプライン補間のフィドルです。 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>
于 2012-10-29T23:47:32.453 に答える