skip to Main Content

i am trying to create a spinning wheel using canvas and html5. its working but my end goal is to try to point the arrow to the exact winning color. so for example if the winning color is the green one, the wheel should turn and stop until the outside arrow faces the winning color. i have attached the code below if someone can or is willing to help.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const spinButton = document.getElementById('spinButton');
const resultDisplay = document.getElementById('result');
const arrow = document.getElementById('arrow');

const wheel = {
    sections: [
        { number: 'Prize 1', color: '#FF4136' },
        { number: 'Prize 2', color: '#0074D9' },
        { number: 'Prize 3', color: '#2ECC40' },
        { number: 'Prize 4', color: '#FFDC00' },
        { number: 'Prize 5', color: '#FF851B' },
        { number: 'Prize 6', color: '#B10DC9' }
    ],
    centerX: canvas.width / 2,
    centerY: canvas.height / 2,
    radius: 200,
    startAngle: 0,
    draw() {
    const angle = (2 * Math.PI) / this.sections.length;
    const textRadius = this.radius - 20; // Radius for positioning text

    for (let i = 0; i < this.sections.length; i++) {
        const textAngle = angle * i + angle / 2; // Angle to position text at the center of each section
        const x = this.centerX + Math.cos(textAngle) * textRadius;
        const y = this.centerY + Math.sin(textAngle) * textRadius;

        ctx.beginPath();
        ctx.moveTo(this.centerX, this.centerY);
        ctx.arc(this.centerX, this.centerY, this.radius, i * angle, (i + 1) * angle);
        ctx.closePath();
        ctx.fillStyle = this.sections[i].color;
        ctx.fill();
        ctx.stroke();
        
        ctx.fillStyle = 'white';
        ctx.font = '20px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(this.sections[i].number, x, y);
    }
}
};

let rotationAngle = 0;
let spinning = false;

function spinWheel() {
    if (spinning) return; // Prevent multiple spins
    
    const randomIndex = Math.floor(Math.random() * wheel.sections.length);
    const result = wheel.sections[randomIndex].number;
    resultDisplay.textContent = `You won: ${result}`;

    // Calculate random rotation angle
    const randomRotation = Math.floor(Math.random() * 720) + 3600;

    // Spin animation
    spinning = true;
    const spinInterval = setInterval(() => {
        rotationAngle += 20; // Change the rotation speed by adjusting this value
        if (rotationAngle >= randomRotation) {
            clearInterval(spinInterval);
            spinning = false;
        }
        drawWheel(rotationAngle);
    }, 20);
}

function drawWheel(rotation) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    wheel.draw();
    ctx.save();
    ctx.translate(wheel.centerX, wheel.centerY);
    ctx.rotate(rotation * Math.PI / 180);
    ctx.translate(-wheel.centerX, -wheel.centerY);
    wheel.draw();
    ctx.restore();
}

wheel.draw(); // Initial drawing
spinButton.addEventListener('click', spinWheel);
#upperContainer {
    position: fixed;
    top: 50px; /* Adjust as needed */
    left: 50px; /* Adjust as needed */
}

#arrow {
    width: 0;
    height: 0;
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
    border-left: 15px solid black; /* Change to left border */
    position: absolute;
    left: -5px; /* Adjust as needed */
    top: 50%;
    transform: translateY(-50%);
}

#canvas {
    border: 1px solid black;
    border-radius: 50%;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wheel of Fortune</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
   <div id="upperContainer">
    <div id="arrow"></div>
    <canvas id="canvas" width="500" height="500"></canvas>
</div>
<button id="spinButton">Spin</button>
<div id="result"></div>

    <script src="script.js"></script>
</body>
</html>

2

Answers


  1. I’ve added a getTargetRotation function to make sure the arrow points to the exact winning colour:

    function getTargetRotation(prizeIndex) {
      const sectionsCount = wheel.sections.length;
      const anglePerSection = 360 / sectionsCount;
      const middleOffset = anglePerSection / 2;
    
      const targetAngle = prizeIndex * anglePerSection + middleOffset;
      const fullRotations = (Math.floor(Math.random() * 3) + 3) * 360;
    
      return fullRotations + targetAngle;
    }
    

    I used your code with this function to create a CodeSandbox for testing, hope it’s helpful
    https://codesandbox.io/p/sandbox/spin-9lcs65?file=%2Fscript.js%3A74%2C20

    Login or Signup to reply.
  2. Since we are guessing the final result, we might as well guess the starting speed. So I start with speed 10 degrees and keep incrementing until I reach the desired one. Why? I introduced some friction to make it spin realistically, and I found it easier to just guess the correct speed iteratively.

    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const spinButton = document.getElementById('spinButton');
    const resultDisplay = document.getElementById('result');
    const arrow = document.getElementById('arrow');
    const toRad = 1 / 180 * Math.PI
    
    const wheel = {
      sections: [{
          number: 'Prize 1',
          color: '#FF4136'
        },
        {
          number: 'Prize 2',
          color: '#0074D9'
        },
        {
          number: 'Prize 3',
          color: '#2ECC40'
        },
        {
          number: 'Prize 4',
          color: '#FFDC00'
        },
        {
          number: 'Prize 5',
          color: '#FF851B'
        },
        {
          number: 'Prize 6',
          color: '#B10DC9'
        }
      ],
      centerX: canvas.width / 2,
      centerY: canvas.height / 2,
      radius: canvas.width / 2,
      startAngle: 0,
      draw() {
        const angle = 360 / this.sections.length;
        const textRadius = this.radius - 20; // Radius for positioning text
    
        for (let i = 0; i < this.sections.length; i++) {
          const textAngle = angle * i + angle / 2; // Angle to position text at the center of each section
          const x = this.centerX + Math.cos(textAngle * toRad) * textRadius;
          const y = this.centerY + Math.sin(textAngle * toRad) * textRadius;
    
          ctx.beginPath();
          ctx.moveTo(this.centerX, this.centerY);
          ctx.arc(this.centerX, this.centerY, this.radius, i * angle * toRad, (i + 1) * angle * toRad);
          ctx.closePath();
          ctx.fillStyle = this.sections[i].color;
          ctx.fill();
          ctx.stroke();
    
          ctx.fillStyle = 'white';
          ctx.font = '20px Arial';
          ctx.textAlign = 'center';
          ctx.textBaseline = 'middle';
          ctx.fillText(this.sections[i].number, x, y);
        }
      }
    };
    
    let rotationAngle = 0;
    let spinning = false;
    
    function spinWheel() {
      if (spinning) return; // Prevent multiple spins
      
      const randomIndex = Math.floor(Math.random() * wheel.sections.length);
      const result = wheel.sections[randomIndex].number;
    
    
      const angle = 360 / wheel.sections.length;
      var rounds = 360 * (Math.floor(Math.random() * 5) + 5);
      const finalAngle = (180 - (angle * randomIndex + Math.random() * (angle - 5))) + rounds
    
      resultDisplay.textContent = `You won: ${result}, current: ${rotationAngle.toFixed(0)}, target Angle: ${finalAngle.toFixed(0)}`;
    
      // Spin animation
      spinning = true
      var deceleration = 0.98
      rotationAngle = rotationAngle % 360
      var speed = calculateSpeed(finalAngle, rotationAngle, deceleration);
    
      const spinInterval = () => {
        drawWheel(rotationAngle);
    
        if (speed <= 0.1) {
          spinning = false;
          resultDisplay.textContent = `You won: ${result}, current: ${rotationAngle.toFixed(0)}, target Angle: ${finalAngle.toFixed(0)}`;
          return
        }
        rotationAngle += speed
        speed *= deceleration
    
    
        requestAnimationFrame(spinInterval)
      }
      spinInterval()
    }
    
    function drawWheel(rotation) {
      rotation = rotation % 360
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.save();
      ctx.translate(wheel.centerX, wheel.centerY);
      ctx.rotate(rotation * toRad);
      ctx.translate(-wheel.centerX, -wheel.centerY);
      wheel.draw();
      ctx.restore();
    }
    
    function calculateSpeed(targetRotationAngle, initialRotationAngle, deceleration) {
      var trySpeed = 10
      while (true) {
    
        var speed = trySpeed
        var current = initialRotationAngle
    
        do {
          current += speed
          speed *= deceleration
        } while (speed > 0.1)
    
        if (Math.abs(current - targetRotationAngle) < 5) {
          return trySpeed;
        }
        trySpeed += 0.1
      }
      return trySpeed
    
    }
    
    
    wheel.draw(); // Initial drawing
    spinButton.addEventListener('click', spinWheel);
    #upperContainer {
      position: fixed;
      top: 50px;
      /* Adjust as needed */
      left: 50px;
      /* Adjust as needed */
    }
    
    #arrow {
      width: 0;
      height: 0;
      border-top: 10px solid transparent;
      border-bottom: 10px solid transparent;
      border-left: 15px solid black;
      /* Change to left border */
      position: absolute;
      left: -5px;
      /* Adjust as needed */
      top: 50%;
      transform: translateY(-50%);
    }
    
    #canvas {
      border: 1px solid black;
    }
    <div id="upperContainer">
      <div id="arrow"></div>
      <canvas id="canvas" width="200" height="200"></canvas>
    </div>
    <button id="spinButton">Spin</button>
    <div id="result"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search