The issue is that your images are loading asynchronously. That means that by the time they are placed on the canvas, the transformation has been changed. They will all end up in the last slice.
A possible solution is to pass the current displacement and rotation to the drawing function. You can then reapply the transform required for the image, independent of the loading order.
Here's an example implementation:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const sectors = [
{color:"#b0f", label:"100", image:"https://i.pinimg.com/originals/0f/74/64/0f7464a556edc8b48d43a8bb604dbc33.png"},
{color:"#f0b", label:"5", image:"https://i.pinimg.com/originals/2a/19/13/2a1913368715b9a2dc1ac75e0f1fd67c.png"},
{color:"#bf0", label:"500", image:"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwhD7Fr24H5ogfjKHDukeqFd4BN2H2f8KPPP3WEa3LQRw_HhLWHSHw_Nr7CSWkME6XQXY&usqp=CAU"},
];
// 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`;
ctx.canvas.width = 600;
ctx.canvas.height = 600;
const dia = ctx.canvas.width;
const rad = dia / 2;
const PI = Math.PI;
const TAU = 2 * PI;
const arc = TAU / sectors.length;
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 acceletare to
let angVel = 0; // Current angular velocity
let ang = 0; // Angle rotation in radians
let isSpinning = false;
let isAccelerating = false;
//* Get index of current sector */
const getIndex = () => Math.floor(tot - ang / TAU * tot) % tot;
const loadImage = (ctx, sector, rad, rot) => {
const img = new Image();
img.onload = function () {
ctx.save();
ctx.resetTransform();
ctx.translate(rad, rad);
ctx.rotate(rot);
ctx.drawImage(img, 150, -25, 50, 50); // Or at whatever offset you like
ctx.restore();
};
img.src = sector.image;
};
//* Draw sectors and prizes texts to canvas */
const drawSector = (sector, i) => {
const ang = arc * i;
// 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
const rot = ang + arc / 2;
ctx.save();
ctx.translate(rad, rad);
ctx.rotate(rot);
ctx.textAlign = "right";
ctx.fillStyle = "#fff";
ctx.font = "bold 30px sans-serif";
ctx.fillText(sector.label, rad - 10, 10);
// IMG
loadImage(ctx, sector, rad, rot);
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;
}
}
ang += angVel; // Update angle
ang %= TAU; // Normalize angle
rotate(); // CSS rotate!
};
const engine = () => {
frame();
requestAnimationFrame(engine)
};
elSpin.addEventListener("click", () => {
if (isSpinning) return;
isSpinning = true;
isAccelerating = true;
angVelMax = rand(0.25, 0.40);
});
// INIT!
sectors.forEach(drawSector);
rotate(); // Initial rotation
engine(); // Start engine!
<!-- language: lang-css -->
#wheelOfFortune {
display: inline-flex;
position: relative;
/* height: 720px;
width: 720px; */
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
# Wheel of fortune using JS Canvas
![wheel of fortune game javascript][1]
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
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 / sectors.length;
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 acceletare to
let angVel = 0; // Current angular velocity
let ang = 0; // Angle rotation in radians
let isSpinning = false;
let isAccelerating = false;
//* 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;
}
}
ang += angVel; // Update angle
ang %= TAU; // Normalize angle
rotate(); // CSS rotate!
};
const engine = () => {
frame();
requestAnimationFrame(engine)
};
elSpin.addEventListener("click", () => {
if (isSpinning) return;
isSpinning = true;
isAccelerating = true;
angVelMax = rand(0.25, 0.40);
});
// INIT!
sectors.forEach(drawSector);
rotate(); // Initial rotation
engine(); // Start engine!
<!-- language: lang-css -->
#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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
[1]: https://i.stack.imgur.com/geKbv.png