animationsvgd3.jsweb-animations

d3 wheel of fortune with infinite rotation or rotation till an event


I have spin wheel made using d3.js.

I want the wheel to rotate infinitely or for a given timer value.

On calling the spin() function under a setTimeout, the wheel does rotates infinitely but it stops with decreasing speed and re-starts slowly. However I want to persist the speed of previous rotation round.

function spin(d){
  $('#spin').on("click", null);          
  if(oldpick.length == data.length){
    console.log("done");
    $('#spin').on("click", null);
    return;
  }

  var  ps       = 360/data.length,
  pieslice = Math.round(1440/data.length),
  rng      = Math.floor((Math.random() * 1440) + 360);

  rotation = (Math.round(rng / ps) * ps);
  const offsetToTop = Math.ceil(360/ps/4) - 1;
  picked = Math.round(data.length - (rotation % 360)/ps) - offsetToTop;
  if(picked >= data.length){
    picked = picked % data.length;
  }else if(picked < 0){
    picked = 0;
  }

  if(oldpick.indexOf(picked) !== -1){
    d3.select(this).call(spin);
    return;
  } else {
    oldpick.push(picked);
  }
  rotation += 90 - Math.round(ps/1);

  vis.transition()
  .duration(1000)
  .attrTween("transform", rotTween)
  .each("end", function(){
    console.log(picked);
    oldrotation = rotation;
    alert(data[picked].xp);          
  });

}

function rotTween(to) {
  var i = d3.interpolate(oldrotation % 360, rotation);
  return function(t) {
    return "rotate(" + i(t) + ")";
  };
}

Please refer the codepen


Solution

  • The default ease for for a d3 transition is "slow-in slow-out" which produces the effect you describe as "stops with decreasing speed and re-starts slowly". It sounds like you want a "linear" transition which uses the same speed throughout. To do that use:

    vis.transition()
      .ease("linear")
      .duration(1000)
      .attrTween("transform", rotTween)
      .each("end", function(){
        console.log(picked);
        oldrotation = rotation;
        alert(data[picked].xp);          
      });
    

    Note, you are using a very old version of d3. The call to .ease would be different with a more modern version. My answer is for the version you are using (v3).

    Edits for comments...

    var padding = { top: 0, right: 0, bottom: 0, left: 0 },
      w = 400 - padding.left - padding.right,
      h = 400 - padding.top - padding.bottom,
      r = Math.min(w, h) / 2,
      currentRotation = 0,
      color = d3.scale
        .ordinal()
        .range([
          "#70d5fc",
          "#afa9e6",
          "#ffc18f",
          "#ffc5ea",
          "#ffe960",
          "#DC8686",
          "#F4BF96"
        ]);
    
    var data = [
      { label: "0x", value: 1, xp: "you Win 0x" },
      { label: "2x", value: 2, xp: "you Win 2x" },
      { label: "4x", value: 3, xp: "you Win 4x" },
      { label: "5x", value: 2, xp: "you Win 5x" },
      { label: "6x", value: 2, xp: "you Win 6x" },
      { label: "8x", value: 3, xp: "you Win 8x" },
      { label: "10x", value: 4, xp: "you Win 10x" },
      { label: "12x", value: 5, xp: "you Win 12x" },
      { label: "13x", value: 5, xp: "you Win 13x" },
      { label: "8x", value: 3, xp: "you Win 8x" },
      { label: "10x", value: 4, xp: "you Win 10x" },
      { label: "12x", value: 5, xp: "you Win 12x" },
      { label: "12.5x", value: 5, xp: "you Win 12.5x" },
      { label: "8x", value: 3, xp: "you Win 8x" },
      { label: "10x", value: 4, xp: "you Win 10x" },
      { label: "12x", value: 5, xp: "you Win 12x" },
      { label: "12x", value: 5, xp: "you Win 12x" }
    ];
    var svg = d3
      .select("#spinwheel")
      .append("svg")
      .data([data])
      .attr("xmlns", "http://www.w3.org/2000/svg")
      .attr("viewBox", "0 0 " + w + " " + w + "")
      .attr("width", w)
      .attr("height", h + padding.top + padding.bottom);
    var container = svg
      .append("g")
      .attr("class", "chartholder")
      .attr(
        "transform",
        "translate(" + (w / 2 + padding.left) + "," + (h / 2 + padding.top) + ")"
      );
    
    var vis = container.append("g");
    
    var pie = d3.layout
      .pie()
      .sort(null)
      .value(function (d) {
        return 1;
      });
    var arc = d3.svg.arc().outerRadius(r);
    var arcs = vis
      .selectAll("g.slice")
      .data(pie)
      .enter()
      .append("g")
      .attr("class", "slice");
    
    arcs
      .append("path")
      .attr("fill", function (d, i) {
        return color(i);
      })
      .attr("d", function (d) {
        return arc(d);
      });
    arcs
      .append("text")
      .attr("transform", function (d) {
        d.innerRadius = 0;
        d.outerRadius = r;
        d.angle = (d.startAngle + d.endAngle) / 2;
        return (
          "rotate(" +
          ((d.angle * 180) / Math.PI - 90) +
          ")translate(" +
          (d.outerRadius - 60) +
          ")"
        );
      })
      .attr("font-size", "20")
      .attr("fill", "#ffffff")
      .attr("text-anchor", "end")
      .text(function (d, i) {
        return data[i].label;
      });
    
    $("#spin").on("click", spin);
    $("#stop").on("click", stop);
    
    function spin(d) {
      $("#spin").attr("disabled", true);
      $("#stop").attr("disabled", null);
      runTransition(currentRotation, currentRotation+360);
    }
    
    function stop(d){
      $("#spin").attr("disabled", null);
      $("#stop").attr("disabled", true);
      vis.interrupt();
    
      const pieSliceInDegrees = 360 / data.length;
            picked = data.length - Math.ceil(currentRotation/pieSliceInDegrees); 
    
       alert(data[picked].xp);
    }
    
    container
      .append("circle")
      .attr("cx", 0)
      .attr("cy", 0)
      .attr("r", 30)
      .style({ fill: "#ffffff" });
    
    // create recursive closure to run transition endlessly
    function runTransition(startR, stopR) {
      
      function rotTween(to) {
        var i = d3.interpolate(startR, stopR);
        return function (t) {
          currentRotation = i(t) % 360;
          return "rotate(" + currentRotation + ")";
        };
      }
      
      vis
        .transition()
        .ease("linear")
        .duration(5000)
        .attrTween("transform", rotTween)
        .each("end", function () {
          runTransition(stopR, stopR+360);
        });
    }
    @import url('https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&display=swap');
    
    button:focus,
    input:focus{
      outline: none;
      box-shadow: none;
    }
    a,
    a:hover{
      text-decoration: none;
    }
    
    body{
      font-family: 'Roboto', sans-serif;
    }
    
    .wheel-spin-box {
        position: relative;
        display: inline-block;
    }
    #spinwheel {
        position: relative;
        width: 360px;
        margin: auto;
    }
    
    #spinwheel{
        position: relative;
        width: 360px;
        margin: auto;
    }
    #spinwheel svg{
        width: 100%;
        height: 100%;
        border: 15px solid #CE3816;
        border-radius: 50%;
        background: #CE3816;
    }
    .chartholder{
    
    }
    
    .spin-click-button {
        background-color: #000;
        font-size: 14px;
        font-weight: 600;
        color: #fff;
        border: none;
        padding: 14px 35px;
        border-radius: 15px;
    }
    
    @media (max-width: 479.98px){
        #spinwheel {
            width: 270px;
        }
    }
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
      <script src="https://d3js.org/d3.v3.min.js"></script>
    </head>
    
    <body>
    
      <div class="container text-center mb-5 mt-5">
        <div class="row">
          <div class="col-md-12">
            <h4><a href="https://codepen.io/piyushpd/pen/abZOjog" target="_blank">Wheel Spin With get value</a></h4>
          </div>
        </div>
      </div>
    
    
    
      <div class="container">
        <div class="row">
          <div class="col-md-12 text-center mb-4">
            <button id="spin" class="spin-click-button">Spin the Wheel</button>
            <button id="stop" class="spin-click-button" disabled="true">Stop the Wheel</button>
          </div>
          <div class="col-md-12 text-center">
            <div class="wheel-spin-box">
              <div id="spinwheel">
    
              </div>
    
            </div>
          </div>
        </div>
      </div>
    </body>
    
    </html>