JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

<span id='games_wheel_prize'>Click the pointy thing and see which segment you land on.</span>
<br>
You've made <span id='games_wheel_spun'>0</span> spins so far.
<br><span id='games_wheel_status'></span>


<div id='games_wheel_container'>
    <div id='games_wheel'></div>
    <button id='games_wheel_spinner'>
        <span>Spin!</span>
        <div id='pointer'></div>
    </button>
</div>

CSS

#games_wheel_container {
    position: relative;
    width: 500px;
    height: 500px;
    margin: 20px auto;
    border: 1px solid white;
    padding: 10px;
    text-align: center;
}

/*
* This div contains the wheel image, and gets rotated.
* Use border-radius: 100% to turn square div round,
* and 'trim' anything outside the circle image.
* You can see the background-color as a 'border'.
*/
#games_wheel {
	position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -250px;
    margin-left: -250px;
    width: 500px;
    height: 500px;
    background-color: green;
    background-image: url('http://griffusion.elementfx.com/pics/ui/wheel.gif');
    border-radius: 100%;
}

/*
* This is the middle bit that user clicks on.
*/
#games_wheel_spinner {
    cursor: pointer;
    color: white;
    font-weight: bold;
    border: none;
    position: absolute;
    width: 50px;
    height: 50px;
    top: 235px;
    left: 235px;
    border-radius: 100%;
    z-index: 2;
    background-color: blue;
}

/*
* The little arrow pointing upwards. Optional.
*/
#games_wheel_spinner #pointer {
    position: absolute;
    width: 0;
    height: 0;
    top: -8px;
    left: 15px;
    border-left: 10px solid transparent;
    border-right: 10px  solid transparent;
    border-bottom: 10px solid blue;
}

JavaScript

(function($) {
  $.fn.fortune = function(args){

    if(args === undefined){
      throw(new Error("You must define the options.prizes"));
    }

    var options = $.extend({}, {
      prices: args,
      duration: 2500,
      separation: 2,
      min_spins: 5,
      max_spins: 15,
      onSpinBounce: function() {}
    }, args);

    var fortune = this;
    var prices_amount = Array.isArray(options.prices)?options.prices.length:options.prices;
    var prices_delta = 360 / prices_amount;
    var is_spinning = false;

    fortune.spin = function(price) {
      price = typeof price === "number"?price:Math.floor(Math.random() * prices_amount);
      var deferred = $.Deferred();
      var position = Math.floor(prices_delta * (price - 1/2) + randomBetween(options.separation, prices_delta - options.separation));
      var spins = randomBetween(options.min_spins, options.max_spins);
      var final_position = 360 * spins + position;
      var prev_position = 0;
      var is_bouncing = false;

      is_spinning = true;

      fortune
      .css({
        "transform": "rotate(" + final_position + "deg)",
        "-webkit-transform": "rotate(" + final_position + "deg)",
        "transition": "transform " + options.duration + "ms cubic-bezier(.17,.67,.12,.99)",
        "-webkit-transition": "-webkit-transform " + options.duration + "ms cubic-bezier(.17,.67,.12,.99)"
      })
      .siblings('.spin').removeClass('bounce');

      var bounceSpin = function() {
        var curPosition = Math.floor(getRotationDegrees(fortune)),
        mod = Math.floor((curPosition + prices_delta*0.5) % prices_delta),
        diff_position,
        position_threshold = prices_delta/5,
        distance_threshold = prices_delta/10;

        prev_position = Math.floor(curPosition < prev_position ? prev_position - 360 : prev_position);
        diff_position = curPosition - prev_position;

        if ((mod < position_threshold && diff_position < distance_threshold) ||
            (mod <...