8

Im trying to create a game of spin the wheel using this tutorial:

http://www.emanueleferonato.com/2015/07/31/create-a-wheel-of-fortune-for-your-html5-games-with-phaser-in-only-a-few-lines/

however, this tutorial used an image for the wheel and i want to create it in html5/js. something vaguely like this:

https://www.winkbingo.com/images/lp/301114/spin_the_wheel_image.png this is what i have so far:

var ctx = canvas.getContext("2d");
var end = 0;
var color = ['#F0F8FF','#FAEBD7','#00FFFF','#7FFFD4','#00FF00','#FF8C00'];
var labels = ['label1', 'label2','label3','label4','label5','label6'];
var slices = 6

for (var i = 0; i < slices; i++) {
ctx.fillStyle = color[i];
ctx.beginPath();
ctx.moveTo(canvas.width/2,canvas.height/2);
ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2,end, ((1/slices)*Math.PI*2)+end ,false);
ctx.lineTo(canvas.width/2,canvas.height/2);
ctx.fill();
end += ((1/slices)*Math.PI*2)+end;
}

i want the number of segments to be able to change by changing the variable slices(between 1-6). and i also want to display the labels on top. then i want to use this canvas instead of the image in that tutorial code so the wheel spins around with the text. hope that wasnt confusing. anyone know how to do this> i dont mind using any libraries etc.

csbk
  • 559
  • 3
  • 9
  • 12

3 Answers3

42

Wheel of fortune using JS Canvas

wheel of fortune game javascript

const sectors = [
  {color:"#f82", label:"Stack"},
  {color:"#0bf", label:"10"},
  {color:"#fb0", label:"200"},
  {color:"#0fb", label:"50"},
  {color:"#b0f", label:"100"},
  {color:"#f0b", label:"5"},
  {color:"#bf0", label:"500"},
];

// Generate random float in range min-max:
const rand = (m, M) => Math.random() * (M - m) + m;

const tot = sectors.length;
const elSpin = document.querySelector("#spin");
const ctx = document.querySelector("#wheel").getContext`2d`;
const dia = ctx.canvas.width;
const rad = dia / 2;
const PI = Math.PI;
const TAU = 2 * PI;
const arc = TAU / tot;
const friction = 0.991;  // 0.995=soft, 0.99=mid, 0.98=hard
const angVelMin = 0.002; // Below that number will be treated as a stop
let angVelMax = 0; // Random ang.vel. to accelerate to 
let angVel = 0;    // Current angular velocity
let ang = 0;       // Angle rotation in radians
let isSpinning = false;
let isAccelerating = false;
let animFrame = null; // Engine's requestAnimationFrame

//* Get index of current sector */
const getIndex = () => Math.floor(tot - ang / TAU * tot) % tot;

//* Draw sectors and prizes texts to canvas */
const drawSector = (sector, i) => {
  const ang = arc * i;
  ctx.save();
  // COLOR
  ctx.beginPath();
  ctx.fillStyle = sector.color;
  ctx.moveTo(rad, rad);
  ctx.arc(rad, rad, rad, ang, ang + arc);
  ctx.lineTo(rad, rad);
  ctx.fill();
  // TEXT
  ctx.translate(rad, rad);
  ctx.rotate(ang + arc / 2);
  ctx.textAlign = "right";
  ctx.fillStyle = "#fff";
  ctx.font = "bold 30px sans-serif";
  ctx.fillText(sector.label, rad - 10, 10);
  //
  ctx.restore();
};

//* CSS rotate CANVAS Element */
const rotate = () => {
  const sector = sectors[getIndex()];
  ctx.canvas.style.transform = `rotate(${ang - PI / 2}rad)`;
  elSpin.textContent = !angVel ? "SPIN" : sector.label;
  elSpin.style.background = sector.color;
};

const frame = () => {

  if (!isSpinning) return;

  if (angVel >= angVelMax) isAccelerating = false;

  // Accelerate
  if (isAccelerating) {
    angVel ||= angVelMin; // Initial velocity kick
    angVel *= 1.06; // Accelerate
  }
  
  // Decelerate
  else {
    isAccelerating = false;
    angVel *= friction; // Decelerate by friction  

    // SPIN END:
    if (angVel < angVelMin) {
      isSpinning = false;
      angVel = 0;
      cancelAnimationFrame(animFrame);
    }
  }

  ang += angVel; // Update angle
  ang %= TAU;    // Normalize angle
  rotate();      // CSS rotate!
};

const engine = () => {
  frame();
  animFrame = requestAnimationFrame(engine)
};

elSpin.addEventListener("click", () => {
  if (isSpinning) return;
  isSpinning = true;
  isAccelerating = true;
  angVelMax = rand(0.25, 0.40);
  engine(); // Start engine!
});

// INIT!
sectors.forEach(drawSector);
rotate(); // Initial rotation
#wheelOfFortune {
  display: inline-flex;
  position: relative;
  overflow: hidden;
}

#wheel {
  display: block;
}

#spin {
  font: 1.5rem/0 sans-serif;
  user-select: none;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  height: 30%;
  margin: -15%;
  background: #fff;
  color: #fff;
  box-shadow: 0 0 0 8px currentColor, 0 0px 15px 5px rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  transition: 0.8s;
}

#spin::after {
  content: "";
  position: absolute;
  top: -17px;
  border: 10px solid transparent;
  border-bottom-color: currentColor;
  border-top: none;
}
<div id="wheelOfFortune">
  <canvas id="wheel" width="300" height="300"></canvas>
  <div id="spin">SPIN asd asd asd as dasd as dasd asd asd as d</div>
</div>

Stop at defined position

Whilst the above example uses requestAnimationFrame, to handle acceleration, deceleration etc, if instead you want to trick the user by controlling the end result and stopping the wheel at a specific angle or approximate sector angle — please refer to this related answer: Stop Wheel of Fortune at Predefined Angle or Index

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
9

Just the plain canvas of html5 has all that is needed to draw a quite good-looking "wheel of fortune" with very little code, no need for libraries.

  • You can draw pie slices using arc
  • You can draw rotated text by using the translate/rotate global transforms
  • you can add shadows and glow effects
  • you can use radial and linear gradients to simulate lighting
  • you can use multi-pass renderings (e.g. rendering a lighting gradient over something already drawn
  • the speed on modern browsers is quite good... fast enough to render a spinning wheel in realtime (I'd expect drawing a single pre-computed rotating image would be faster especially on mobile devices, this approach would however require to have shadows in another canvas on top of the rotating wheel).

For example this is what I got with ~100 html lines (3.5k NOT minified) in total:

html5 wheel of fortune

Live example at http://raksy.dyndns.org/wheel.html

6502
  • 112,025
  • 15
  • 165
  • 265
  • Impressive glow, shadow & shine effects -- plus easing!! – markE Nov 23 '15 at 02:38
  • Hi, I was working with your code from days now trying to figure out how to do you come up with your formula which selects the prize in the correct angle. Is the speed control which prize will be selected? – POGI Apr 04 '20 at 08:36
  • @POGI: I reread my code and indeed yes, the `speed` decides which sector the needle will end up being. This is also what happens in a real wheel of fortune, but probably a better (guaranteed uniform) way would be to choose the final sector and then just playing an animation to reach it. – 6502 Apr 04 '20 at 12:11
  • @6502 Thank you for the response. "To choose the final sector" This was actually I'm trying to do but still no progress. In your code you have this formula: "angle = angle0 + (speed*t - 0.5*speed*t*t)*100". If you don't mind, can you explain how you come up with that? Thank you so much for your help and very much appreciated. – POGI Apr 04 '20 at 17:18
  • My plan is to create an API that will determine the prize but I don't know what value/number I need to return. Here's actual code I'm working on https://shorturl.do/jJnkX – POGI Apr 04 '20 at 17:22
  • 1
    @POGI: I've update the source code so that now the `spin` call accepts a parameter the winning index and the time for the animation. The wheel will make approximately 5 full turns before stopping at the required winner index using an sigmoid based animation. In case you're not seeing any change in the source force reloading the page. – 6502 Apr 04 '20 at 20:22
  • Thank you so much for your help. I've checked it and it's great. Finally I can move forward on this. Thank you again. – POGI Apr 05 '20 at 08:37
  • How do i change centre red dot with a circular logo ? and how do i change shape of the prize pointer? – ghost Sep 27 '22 at 11:22
4

I am the creator of a Javascript library called Winwheel.js which allows easy creation of winning/prize wheels on HTML canvas.

The wheels can be code drawn or use graphically rich images and it leverages Greensock's animation platform for the spinning and other animation effects.

Here is an example of how easy it is to create a basic code-drawn wheel using Winwheel.js...

var theWheel = new Winwheel({
  'numSegments'  : 8,
  'segments'     :
  [
    {'fillStyle' : '#eae56f', 'text' : 'Prize 1'},
      {'fillStyle' : '#89f26e', 'text' : 'Prize 2'},
      {'fillStyle' : '#7de6ef', 'text' : 'Prize 3'},
      {'fillStyle' : '#e7706f', 'text' : 'Prize 4'},
      {'fillStyle' : '#eae56f', 'text' : 'Prize 5'},
      {'fillStyle' : '#89f26e', 'text' : 'Prize 6'},
      {'fillStyle' : '#7de6ef', 'text' : 'Prize 7'},
      {'fillStyle' : '#e7706f', 'text' : 'Prize 8'}
  ],
  'animation' :
  {
    'type'     : 'spinToStop',
    'duration' : 5,
    'spins'    : 8
  }
});

There is a full set of tutorials for Winwheel.js to help you get started on my site. If you are interested please visit http://dougtesting.net/

dougtesting.net
  • 571
  • 4
  • 9
  • I am using winwheel.js for my application and I wanted to apply gradient colors for each segment. Is there a way I can do that? – user3878988 Feb 26 '18 at 00:27
  • Hi @user3878988 yes it should be possible; I experimented years ago with gradients and they worked. You can set the fillStyle of each segment to a gradient as seen in the "More Examples" section of https://www.w3schools.com/tags/canvas_fillstyle.asp – dougtesting.net Feb 26 '18 at 01:16
  • Hi @dougtesting.net, thank you so much for replying. From the example, it seems that the gradient is set to the whole canvas, however, my requirement is to set different gradients for each segment. Is there any example on your website for this that I can refer. Thank you again for your help on this. – user3878988 Feb 26 '18 at 01:24
  • @user3878988 sorry I have no examples of gradients on my site. Now that you remind me, thinking back some years to when I fiddled around with it, there was something funky with trying to do a gradient for each individual segment. Honestly not sure if I figured out a solution. – dougtesting.net Feb 26 '18 at 03:03
  • Hi @dougtesting.net How can I integrate this in Reactjs can you please give me some example of that? – Zaif Warm Aug 31 '20 at 09:52