\$\begingroup\$
\$\endgroup\$
1
I build project Case opening simulator in React JS + tailwind. I am looking for maybe another way to implement the structure of code and tell me if I've done something wrong, what I should do better or to add something more.
import React, { useState, useEffect } from 'react';
import ResultDisplay from './ResultDisplay';
import MenuButton from './MenuButton';
import Instructions from './Instructions';
import Controls from './Controls';
import { calculateResponsiveSizes } from './ResponsiveUtils';
const CaseOpener = () => {
const itemsAmount = 100;
const [ItemsList, setItemsList] = useState([]);
const [elementWidth, setElementWidth] = useState(200);
const [reelContainerWidth, setReelContainerWidth] = useState(1000);
const [winningItem, setWinningItem] = useState(null);
const [reelItems, setReelItems] = useState([]);
const [isOpening, setIsOpening] = useState(false);
const [hasOpened, setHasOpened] = useState(false);
const [selectedCase, setselectedCase] = useState(null);
const [randomSpin, setRandomSpin] = useState(0);
const spinDuration = 10;
const minSpin = -13000;
const maxSpin = -12050;
const baseRandomSpin = Math.floor(Math.random() * (maxSpin - minSpin + 1)) + minSpin;
// loading skin data
const goToCase = () => {
setselectedCase(true);
};
// window size change chandler
useEffect(() => {
const handleResize = () => {
if (!isOpening) { // tylko gdy nie jest w trakcie otwierania
calculateResponsiveSizes(setElementWidth, setReelContainerWidth, setRandomSpin, baseRandomSpin);
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [isOpening, baseRandomSpin]);
const openCase = async () => {
setIsOpening(true);
setHasOpened(false);
const baseElementWidth = 200;
const newRandomSpin = (baseRandomSpin * elementWidth) / baseElementWidth;
setRandomSpin(newRandomSpin);
try {
const response = await fetch('http://localhost:5000/open-case-batch');
const data = await response.json();
setItemsList(data);
console.log("Wylosowane przedmioty:", data);
const generatedItems = [];
for(let i = 0; i < itemsAmount; i++) {
generatedItems.push(data[i]);
}
setReelItems(generatedItems);
console.log("-----\\\\\\\\\\\\\\\\\\\\\\\\\\-----");
const OFFSET_ELEMENTS = -2;
const OFFSET_PX = OFFSET_ELEMENTS * elementWidth;
// calc index of winning item
const index = Math.floor((-newRandomSpin - OFFSET_PX + elementWidth / 2) / elementWidth);
console.log("randomSpin:", randomSpin);
console.log("elementWidth:", elementWidth);
console.log("winning index:", index);
setWinningItem(generatedItems[index]);
console.log("winning item: ", generatedItems[index]?.skin_name); // Changed from 'name' to 'skin_name'
console.log("Posibble floats: ",generatedItems[index].float_min," - ",(generatedItems[index].float_max));
console.log("Skin float: ",generatedItems[index].float);
console.log(generatedItems[index].condition);
// console.log(winningItem);
setTimeout(() => {
setIsOpening(false);
setHasOpened(true);
}, spinDuration * 1000);
} catch (error) {
console.error("Błąd podczas pobierania przedmiotów:", error);
setIsOpening(false);
}
};
//delete
const Calculator = () => {
useEffect(() => {
const calculate = async () => {
try {
const response = await fetch('http://localhost:5000/calculate');
const data = await response.json();
console.log('Wynik z backendu:', data);
} catch (error) {
console.error('error loading file:', error);
}
};
calculate();
}, []);
return null; // nic nie renderuje
};
const resetCase = () => {
setIsOpening(false);
setHasOpened(false);
setReelItems([]);
};
const generateReel = () => {
if (reelItems.length === 0) {
return [];
}
const items = [];
for (let i = 0; i < reelItems.length; i++) {
const item = reelItems[i];
items.push(
<div
key={i}
className="flex-shrink-0 bg-gradient-to-br from-gray-600 to-[#4a90e2] rounded-lg
border border-gray-700 flex items-center justify-center shadow-lg"
style={{
width: `${elementWidth}px`,
height: `${elementWidth * 0.8}px`
}}
>
<img
src={item.image_url}
alt={item.skin_name}
className="object-cover rounded-md"
style={{
width: `${elementWidth * 0.7}px`,
height: `${elementWidth * 0.56}px`
}}
/>
</div>
);
}
return items;
};
return (
<>
<div className="h-20 flex items-center relative overflow-hidden bg-[#26262B] justify-center">
<div className="hidden sm:flex">
<MenuButton>Menu</MenuButton>
<MenuButton>Daily bonus</MenuButton>
<MenuButton>Contracts</MenuButton>
<MenuButton>Market</MenuButton>
<MenuButton>Inventory</MenuButton>
</div>
<div className="sm:hidden">
<MenuButton>☰</MenuButton>
</div>
</div>
<div className="min-h-[calc(100vh-5rem)] flex items-center justify-center
relative overflow-hidden bg-[#1a1a1a] border-t border-black px-4">
<div className="bg-[#2a2a2a] rounded-2xl shadow-2xl p-4 sm:p-8 lg:p-16 w-full max-w-7xl
border-2 border-[#1a1a2a]">
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-bold text-center mb-8 text-[#f8f9fa]">
Case Opening
</h1>
{!selectedCase && (
<div className="mb-8 text-center">
<label className="inline-block bg-[#f39c12]
hover:bg-[#e67e22] text-white font-bold py-3 px-6 rounded-lg cursor-pointer transition-colors duration-200
border-2 border-[#f39c12]
transform hover:scale-105" onClick={goToCase}
>
Select this case
</label>
</div>
)}
{/* Case Opening Area */}
{selectedCase && (
<div className="mb-8">
{/* Reel Container */}
<div className="relative bg-[#333333] rounded-xl p-4 sm:p-8 lg:p-16 mb-6 overflow-hidden flex justify-center">
{/* Selection Line */}
<div
id='marker'
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-0.5 bg-[#4a90e2] z-10 shadow-lg"
style={{ height: `${elementWidth * 0.8}px` }}
></div>
{/* Reel */}
<div
id='koniec'
style={{ width: `${elementWidth * 5}px`,height: `${elementWidth * 0.8}px` }}
className="relative overflow-hidden"
>
<div
className={`flex ${isOpening ? 'animate-spin-reel' : ''}`}
>
{generateReel()}
</div>
</div>
</div>
<Controls
onOpen={openCase}
onReset={resetCase}
onChangeImage={() => setselectedCase(null)}
isOpening={isOpening}
/>
<ResultDisplay hasOpened={hasOpened} winningItem={winningItem}></ResultDisplay>
</div>
)}
<Instructions selectedCase={selectedCase} />
</div>
<style jsx>{`
@keyframes spin-reel {
0% { transform: translateX(0px); }
100% { transform: translateX(${randomSpin}px); }
}
.animate-spin-reel {
animation: spin-reel 9.5s cubic-bezier(0.33, 1, 0.68, 1) forwards;
}
`}</style>
</div>
</>
);
};
export default CaseOpener;
ResponsiveUtils.jsx:
export const calculateResponsiveSizes = (
setElementWidth,
setReelContainerWidth,
setRandomSpin,
baseRandomSpin
) => {
const screenWidth = window.innerWidth;
let newElementWidth;
let newContainerWidth;
if (screenWidth < 640) {
newElementWidth = 120;
newContainerWidth = screenWidth * 0.8;
} else if (screenWidth < 1024) {
console.log("tablet");
newElementWidth = 150;
newContainerWidth = screenWidth * 0.7;
} else if (screenWidth < 1296) {
console.log("pc small screen");
newElementWidth = 170;
newContainerWidth = screenWidth * 0.7;
} else {
console.log("pc");
newElementWidth = 200;
newContainerWidth = 1000;
}
setElementWidth(newElementWidth);
setReelContainerWidth(newContainerWidth);
const baseElementWidth = 200;
const newRandomSpin = (baseRandomSpin * newElementWidth) / baseElementWidth;
setRandomSpin(newRandomSpin);
};
-
\$\begingroup\$ I think it should be possible to make the game responsive entirely in CSS. \$\endgroup\$Kokodoko– Kokodoko2025年07月22日 10:39:16 +00:00Commented Jul 22 at 10:39
default