I have this component that should simulate a slot machine to rotate to the numbered face to the number that is passed into props.slots
.
It works the first time but doesn't seem to work after the initial spin. I can never predict even by hand which number face it will land on. What's wrong with my math?
I have 5 faces so I divided 360/5 = 72 deg in between each face.
Then have (current - next) * 72deg
but this doesn't give me the correct face!
const {
useEffect,
useRef,
useState
} = React;
function rnd(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
const SlotMachine = (props) => {
const ringElements = useRef();
const [state, setState] = useState({
manual: true,
rings: [],
slots: []
});
const slotMap = {
0: 0,
1: 72,
2: 144,
3: 216,
4: 288
};
useEffect(() => {
const newState = { ...state,
slots: props.slots,
manual: false
};
setState(newState);
}, [props.slots]);
useEffect(() => {
if (state.manual === false) {
rotate();
}
setState({ ...state,
manual: true
});
}, [state.manual]);
const rotate = () => {
let reRing = [];
state.rings.forEach((el, i) => {
console.log(`--- RING ${i} ----`);
console.log(`----ROTATE FROM ${state.rings[i].curr} ROTATE TO ${state.slots[i]} Face ---`);
console.log(state);
let obj = el;
let element = obj.ringElement;
console.log(element);
let turns = (obj.curr - state.slots[i]);
let move = (obj.curr === null) ? slotMap[state.slots[i]] : (turns * 72);
console.log(`TURN BY ${turns}`);
//move += (360 * rnd(3,6));
console.log(`rotateX(${move}deg) for ring${i}`);
element.style.transform = `rotateX(${move}deg)`;
console.log(`storing ${state.slots[i]} as the obj.curr replacing ${obj.curr}`);
obj.curr = state.slots[i];
reRing = [...reRing, obj];
});
console.log('update rings:');
setState({ ...state,
rings: reRing
});
}
useEffect(() => {
let mountRings = [];
Array.from(ringElements.current.children).forEach((el, i) => {
let obj = {};
obj.ringElement = el;
obj.curr = null;
mountRings = [...mountRings, obj];
});
console.log(mountRings);
setState({ ...state,
rings: mountRings
});
}, []);
return(
<div>
<div className="slots">
<div className="rings" ref={ringElements}>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
</div>
</div>
<button className="spin-button" onClick={() => { state.manual ? rotate() : setState({...state, manual:true }) }}>SPIN</button>
</div>
);
}
ReactDOM.render(
<SlotMachine slots={3} />,
document.getElementById('app')
);
:root {
--pushZ: 27vmin;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Rozha One", serif;
font-size: 4vmin;
color: #d99748;
user-select: none;
}
body,
.slots,
.rings,
.ring,
.slot,
.spin {
display: flex;
justify-content: center;
align-items: center;
transform-style: preserve-3d;
}
body {
flex-direction: column;
}
body {
justify-content: space-around;
width: 100%;
height: 100vh;
background: #1F1F1F;
}
.slots {
position: relative;
width: 120vmin;
height: 50vmin;
}
.slots>* {
height: 100%;
}
.rings {
width: 50%;
perspective: 250vmin;
}
.ring {
position: relative;
width: calc(100% / 3);
height: 100%;
transition: 3s ease-in-out;
}
.slot {
position: absolute;
width: 100%;
height: 45%;
font-size: 15vmin;
background: rgba(31, 31, 31, 0.935);
}
/* rotate goes from away */
.slot:nth-child(1) {
color: red;
transform: rotateX(360deg) translateZ(var(--pushZ));
}
.slot:nth-child(2) {
color: green;
transform: rotateX(288deg) translateZ(var(--pushZ));
}
.slot:nth-child(3) {
color: blue;
transform: rotateX(216deg) translateZ(var(--pushZ));
}
.slot:nth-child(4) {
color: purple;
transform: rotateX(144deg) translateZ(var(--pushZ));
}
.slot:nth-child(5) {
color: orange;
transform: rotateX(72deg) translateZ(var(--pushZ));
}
.spin-button {
width: 14vmin;
height: 14vmin;
transform: rotateX(45deg);
border: 0.3333333333vmin solid;
border-radius: 50%;
font-size: 4.5454545455vmin;
color: #b57e3c;
background: transparent;
outline: none;
cursor: pointer;
transition: 0.3s;
}
.spin-button:hover {
color: #d99748;
}
.spin-button:active {
color: #ffca60;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Solved my issue! Rotation here always starts from the "origin". There is no need to reorient from the last rotation.
element.style.transform = `rotateX(${move}deg)`;