Persist dice state with localStorage and improve rendering layering.

Added functionality to save and load dice state (d1, d2, special) using localStorage for better user experience. Adjusted component styles to improve layering, ensure proper z-index for elements, and enhance 3D dice rendering.
This commit is contained in:
2026-02-08 11:11:50 +01:00
parent 56944bc8d7
commit 81065f1054
2 changed files with 36 additions and 6 deletions

View File

@@ -544,11 +544,29 @@ export default function App() {
// ✅ 3 Würfel: 2x d6 + 1x Spezial (Häuser + Hilfkarte + Dunkles Deck) // ✅ 3 Würfel: 2x d6 + 1x Spezial (Häuser + Hilfkarte + Dunkles Deck)
const DicePanel = ({ onRoll }) => { const DicePanel = ({ onRoll }) => {
const LS_KEY = "hp_cluedo_dice_v1";
const [d1, setD1] = useState(4); const [d1, setD1] = useState(4);
const [d2, setD2] = useState(2); const [d2, setD2] = useState(2);
const [special, setSpecial] = useState("gryffindor"); const [special, setSpecial] = useState("gryffindor");
const [rolling, setRolling] = useState(false); const [rolling, setRolling] = useState(false);
useEffect(() => {
try {
const raw = localStorage.getItem(LS_KEY);
if (!raw) return;
const parsed = JSON.parse(raw);
if (parsed?.d1) setD1(parsed.d1);
if (parsed?.d2) setD2(parsed.d2);
if (parsed?.special) setSpecial(parsed.special);
} catch {}
}, []);
useEffect(() => {
try {
localStorage.setItem(LS_KEY, JSON.stringify({ d1, d2, special }));
} catch {}
}, [d1, d2, special]);
const specialFaces = ["gryffindor", "slytherin", "ravenclaw", "hufflepuff", "help", "dark"]; const specialFaces = ["gryffindor", "slytherin", "ravenclaw", "hufflepuff", "help", "dark"];
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
@@ -650,7 +668,7 @@ export default function App() {
"radial-gradient(120% 120% at 20% 10%, rgba(255,255,255,0.12), rgba(0,0,0,0.35) 55%, rgba(0,0,0,0.62))", "radial-gradient(120% 120% at 20% 10%, rgba(255,255,255,0.12), rgba(0,0,0,0.35) 55%, rgba(0,0,0,0.62))",
boxShadow: "0 18px 50px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(255,255,255,0.06)", boxShadow: "0 18px 50px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(255,255,255,0.06)",
position: "relative", position: "relative",
overflow: "hidden", overflow: "visible", // ✅ WICHTIG für Fix 4
display: "grid", display: "grid",
placeItems: "center", placeItems: "center",
cursor: rolling ? "default" : "pointer", cursor: rolling ? "default" : "pointer",
@@ -659,7 +677,6 @@ export default function App() {
outline: "none", outline: "none",
}} }}
disabled={rolling} disabled={rolling}
title={rolling ? "Rolling…" : "Roll"}
> >
{/* inner ring */} {/* inner ring */}
<div <div
@@ -670,6 +687,7 @@ export default function App() {
border: `1px solid ${ringColor}`, border: `1px solid ${ringColor}`,
opacity: 0.85, opacity: 0.85,
pointerEvents: "none", pointerEvents: "none",
zIndex: 1,
}} }}
/> />
@@ -683,6 +701,7 @@ export default function App() {
opacity: 0.9, opacity: 0.9,
pointerEvents: "none", pointerEvents: "none",
mixBlendMode: "screen", mixBlendMode: "screen",
zIndex: 1,
}} }}
/> />
@@ -695,14 +714,19 @@ export default function App() {
"linear-gradient(120deg, rgba(255,255,255,0.10) 0%, transparent 38%, transparent 70%, rgba(255,255,255,0.06) 100%)", "linear-gradient(120deg, rgba(255,255,255,0.10) 0%, transparent 38%, transparent 70%, rgba(255,255,255,0.06) 100%)",
opacity: 0.8, opacity: 0.8,
pointerEvents: "none", pointerEvents: "none",
zIndex: 1,
}} }}
/> />
{children} {/* 👇 Cube kommt IMMER darüber */}
<div style={{ position: "relative", zIndex: 2 }}>
{children}
</div>
</button> </button>
); );
}; };
/* map value->cube rotation so that value face is FRONT /* map value->cube rotation so that value face is FRONT
Face layout: Face layout:
front=1, back=6, top=2, bottom=5, right=3, left=4 front=1, back=6, top=2, bottom=5, right=3, left=4
@@ -755,7 +779,7 @@ export default function App() {
return ( return (
<DieShell ringColor="rgba(242,210,122,0.18)" rolling={rolling} onClick={onClick}> <DieShell ringColor="rgba(242,210,122,0.18)" rolling={rolling} onClick={onClick}>
<div className="dieCubeWrap"> <div className="dieCubeWrap" style={{ zIndex: 2 }}>
<div <div
className={`dieCube ${rolling ? "rolling" : ""}`} className={`dieCube ${rolling ? "rolling" : ""}`}
style={{ "--rx": rot.rx, "--ry": rot.ry }} style={{ "--rx": rot.rx, "--ry": rot.ry }}

View File

@@ -79,10 +79,11 @@ body {
/* Dice overlay: under board slightly right */ /* Dice overlay: under board slightly right */
.diceOverlay { .diceOverlay {
position: absolute; position: absolute;
overflow: visible;
bottom: 14px; bottom: 14px;
right: 18px; right: 18px;
width: 220px; width: 220px;
pointer-events: none; pointer-events: auto;
opacity: 0.98; opacity: 0.98;
} }
@@ -186,6 +187,10 @@ body {
transform: translateY(-3px) rotateX(8deg) rotateY(-10deg); transform: translateY(-3px) rotateX(8deg) rotateY(-10deg);
} }
.diceRow3d {
overflow: visible;
}
/* Cube container inside the button */ /* Cube container inside the button */
.dieCubeWrap { .dieCubeWrap {
width: 44px; width: 44px;
@@ -209,9 +214,10 @@ body {
@keyframes cubeRoll { @keyframes cubeRoll {
0% { transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); } 0% { transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }
100% { 100% {
/* WICHTIG: NUR Vielfache von 360°, damit Ende = gleiche Orientierung */
transform: transform:
rotateX(calc(var(--rx, 0deg) + 720deg)) rotateX(calc(var(--rx, 0deg) + 720deg))
rotateY(calc(var(--ry, 0deg) + 540deg)); rotateY(calc(var(--ry, 0deg) + 720deg));
} }
} }