DEV Community

Cover image for Wheel of Fortune with CSS

Wheel of Fortune with CSS

Mads Stoumann on May 23, 2024

A "Wheel of Fortune" component just popped up in my feed. I always spin, but never win! Anyway, this type of component is often built with <canv...
Collapse
 
afif profile image
Temani Afif

The height calculation is actually not correct. You need to consider the polygon shape around the circle to find the correct height (the circumscribed polygon)

It's equal to height: calc(2*50cqi*tan(180deg/var(--_items))); that you can simplify by setting the ratio aspect-ratio: 1/calc(2*tan(180deg/var(--_items))); to avoid using the width value twice.

With this you won't have issue when you apply the clip-path

Collapse
 
madsstoumann profile image
Mads Stoumann • Edited

AH, perfect — thank you! You truly are the master of CSS Shapes. I've added an update, and will use your input for the follow-up article with spinning.

Collapse
 
vladyslav_dev_1c0ce7bcc6c profile image
Vladyslav Dev

I really like your implementation because all libs create a wheel using canvas but i need the one to be highly customizable. Please help me to add logic to spin the wheel to the predefined (from backend or mocked) sector!

Collapse
 
madsstoumann profile image
Mads Stoumann

You just need to set newEndDegree manually, matching your predefined destination.

Collapse
 
martin_prihoda_2e16335cab profile image
Martin Prihoda

Has anyone solved the return value of the winning field? For example index for li tag. I have something, but the results are +- one position.

Collapse
 
martin_prihoda_2e16335cab profile image
Martin Prihoda

I think it’s resolved ( Link on Codepen.io ).

// --------------------------------------------
// append code for getting index of wheel spim
// --------------------------------------------
const rangeAngles = [
  {index:  0, from: 345, to: 360},
  {index:  0, from:   0, to:  15},
  {index:  1, from:  15, to:  45},
  {index:  2, from:  45, to:  75},
  {index:  3, from:  75, to: 105},
  {index:  4, from: 105, to: 135},
  {index:  5, from: 135, to: 165},
  {index:  6, from: 165, to: 195},
  {index:  7, from: 195, to: 225},
  {index:  8, from: 225, to: 255},
  {index:  9, from: 255, to: 285},
  {index: 10, from: 285, to: 315},
  {index: 11, from: 315, to: 345},
];

// position at zero level
const calculateZeroAngle = (finalAngle) => {
  let zeroAngle = 360 - finalAngle + 90;
  zeroAngle = ((zeroAngle % 360) + 360) % 360;
  zeroAngle = Math.round(zeroAngle * 10) / 10;
  return zeroAngle;
};

// Save the final degree to determine the winning segment
animation.onfinish = async () => {
  const finalAngle = ((newEndDegree % 360) + 360) % 360;
  const zeroAngle = calculateZeroAngle( finalAngle );
  const indexfOfWinner = rangeAngles.find( a => a.from < zeroAngle && a.to >= zeroAngle ).index
  alert( `Index of Winner: ${indexfOfWinner}` )
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
madsstoumann profile image
Mads Stoumann
const normalizeAngle = (finalAngle) => {
  return (360 - finalAngle + 90) % 360;
};

const items = wheel.children.length;
const segment = 360 / items;
const offset = 15;

animation.onfinish = () => {
  const finalAngle = newEndDegree % 360;
  const normalizedAngle = normalizeAngle(finalAngle);
  const winner = Math.floor(((normalizedAngle + offset) % 360) / segment);
  console.log(wheel.children[winner].textContent);
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
madsstoumann profile image
Mads Stoumann

Cool!

Collapse
 
madsstoumann profile image
Mads Stoumann

Please share — I need to think about it too!

Collapse
 
jocomvag profile image
Jocom Vag

Always here for an interesting CSS project. Kudos.

Collapse
 
madsstoumann profile image
Mads Stoumann

Thanks!

Collapse
 
ddebajyati profile image
Debajyati Dey

wholesome wheel! :)
Beautiful project!

Collapse
 
madsstoumann profile image
Mads Stoumann

Thank you!

Collapse
 
troy_forsyth_e0b0c56bfbf0 profile image
Troy Forsyth

So I found this posting a few days ago. I used the code pen and am trying to make this into a dynamically loading setup. Everything is fine with the wedges until you get below 4 slices. At 3 and less it breaks. I feel like it's something to do with the aspect ration of the LI tags.

Image description

Any thoughts?

Collapse
 
efpage profile image
Eckehard

I suppose this would be easier done in pure Javasript using CSS only where It's appropriate (e.g. the animation). Is it really worth the effort doing anything in CSS?

Collapse
 
lcsga profile image
LcsGa

I don't think that'd be easier in javascript. Whenever you have something visual, the right tool for that is CSS. When it's easier in pure JS, this usually mean that you lack knowledge with CSS.

Collapse
 
efpage profile image
Eckehard

Oh, have fun to rebuild the [solarsystem][dev.to/cookiemonsterdev/solar-syst...] with pure CSS. There are good reasons the S in
CSS comes from "style", not from graphics...