dobokocka mukodik :O #56

Merged
Walke merged 1 commits from dice into main 2025-10-20 19:40:14 +02:00
3 changed files with 157 additions and 91 deletions
@@ -66,12 +66,17 @@ const GameScreen = () => {
{ id: 3, name: "Fürtös", position: 68, score: 14, color: "bg-yellow-600", emoji: "😂" }, { id: 3, name: "Fürtös", position: 68, score: 14, color: "bg-yellow-600", emoji: "😂" },
]) ])
// New: selected dice value from dropdown (null = none)
const [selectedDice, setSelectedDice] = useState(null)
// Sort players by position in descending order // Sort players by position in descending order
const sortedPlayers = [...players].sort((a, b) => b.position - a.position) const sortedPlayers = [...players].sort((a, b) => b.position - a.position)
// Handle dice roll // Handle dice roll completion
const handleDiceRoll = (value) => { const handleDiceRoll = (value) => {
console.log("Rolled:", value) console.log("Rolled:", value)
// reset dropdown selection after roll
setSelectedDice(null)
// You can add logic here to move the current player based on the dice value // You can add logic here to move the current player based on the dice value
} }
@@ -118,9 +123,6 @@ const GameScreen = () => {
{/* Háttér */} {/* Háttér */}
<div className="absolute w-full h-full opacity-10 pointer-events-none overflow-hidden"> <div className="absolute w-full h-full opacity-10 pointer-events-none overflow-hidden">
{[...Array(35)].map((_, i) => ( {[...Array(35)].map((_, i) => (
// Sajat pulse effect! => node_modules/tailwindcss/index.css:
// --animate-pulse8: pulse 6s cubic-bezier(0.4, 0.2, 0.6, 1) infinite;
<div <div
key={i} key={i}
className="absolute rounded-full bg-teal-600 animate-pulse8" className="absolute rounded-full bg-teal-600 animate-pulse8"
@@ -222,8 +224,31 @@ const GameScreen = () => {
{/* Dice Container */} {/* Dice Container */}
<div className="bg-gray-800 rounded-xl p-4 shadow-lg border border-teal-700 text-center"> <div className="bg-gray-800 rounded-xl p-4 shadow-lg border border-teal-700 text-center">
<h2 className="text-xl font-semibold mb-3 text-teal-300">Dobókocka</h2> <h2 className="text-xl font-semibold mb-3 text-teal-300">Dobókocka</h2>
<p className="text-gray-300 text-sm mb-4">Kattints a kockára dobáshoz!</p> <p className="text-gray-300 text-sm mb-4">
<Dice onRoll={handleDiceRoll} /> Kattints a kockára dobáshoz vagy válassz egy számot az alábbiból!
</p>
{/* Dropdown to select number 1-6 (triggers animated roll to that number) */}
<div className="mb-3">
<select
value={selectedDice ?? ""}
onChange={(e) => {
const v = e.target.value ? Number(e.target.value) : null
setSelectedDice(v)
}}
className="bg-gray-900 text-gray-200 rounded-md p-2 border border-gray-700"
>
<option value="">Válassz számot...</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
<Dice onRoll={handleDiceRoll} selectedValue={selectedDice} />
</div> </div>
</div> </div>
</div> </div>
+97 -65
View File
@@ -1,74 +1,103 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from "react"
import { dotPositions } from "./diceDotPositions"
const Dice = ({ onRoll }) => { const Dice = ({ onRoll, selectedValue }) => {
const [diceValue, setDiceValue] = useState(1); const [diceValue, setDiceValue] = useState(1)
const [isRolling, setIsRolling] = useState(false); const [isRolling, setIsRolling] = useState(false)
const animationRef = useRef(null); const animationRef = useRef(null)
const rollTimeoutRef = useRef(null); const rollTimeoutRef = useRef(null)
const diceFaces = [ const diceFaces = [
[<div key="center" className="dice-dot"></div>], [<div key="center" className="dice-dot" style={dotPositions.center}></div>],
[<div key="top-left" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot"></div>], [
[<div key="top-left" className="dice-dot"></div>, <div key="center" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot"></div>], <div key="top-left" className="dice-dot" style={dotPositions.topLeft}></div>,
[<div key="top-left" className="dice-dot"></div>, <div key="top-right" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot" style={dotPositions.bottomRight}></div>,
<div key="bottom-left" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot"></div>], ],
[<div key="top-left" className="dice-dot"></div>, <div key="top-right" className="dice-dot"></div>, [
<div key="center" className="dice-dot"></div>, <div key="top-left" className="dice-dot" style={dotPositions.topLeft}></div>,
<div key="bottom-left" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot"></div>], <div key="center" className="dice-dot" style={dotPositions.center}></div>,
[<div key="top-left" className="dice-dot"></div>, <div key="top-right" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot" style={dotPositions.bottomRight}></div>,
<div key="middle-left" className="dice-dot"></div>, <div key="middle-right" className="dice-dot"></div>, ],
<div key="bottom-left" className="dice-dot"></div>, <div key="bottom-right" className="dice-dot"></div>] [
]; <div key="top-left" className="dice-dot" style={dotPositions.topLeft}></div>,
<div key="top-right" className="dice-dot" style={dotPositions.topRight}></div>,
<div key="bottom-left" className="dice-dot" style={dotPositions.bottomLeft}></div>,
<div key="bottom-right" className="dice-dot" style={dotPositions.bottomRight}></div>,
],
[
<div key="top-left" className="dice-dot" style={dotPositions.topLeft}></div>,
<div key="top-right" className="dice-dot" style={dotPositions.topRight}></div>,
<div key="center" className="dice-dot" style={dotPositions.center}></div>,
<div key="bottom-left" className="dice-dot" style={dotPositions.bottomLeft}></div>,
<div key="bottom-right" className="dice-dot" style={dotPositions.bottomRight}></div>,
],
[
<div key="top-left" className="dice-dot" style={dotPositions.topLeft}></div>,
<div key="top-right" className="dice-dot" style={dotPositions.topRight}></div>,
<div key="middle-left" className="dice-dot" style={dotPositions.middleLeft}></div>,
<div key="middle-right" className="dice-dot" style={dotPositions.middleRight}></div>,
<div key="bottom-left" className="dice-dot" style={dotPositions.bottomLeft}></div>,
<div key="bottom-right" className="dice-dot" style={dotPositions.bottomRight}></div>,
],
]
useEffect(() => { useEffect(() => {
return () => { return () => {
if (animationRef.current) cancelAnimationFrame(animationRef.current); if (animationRef.current) cancelAnimationFrame(animationRef.current)
if (rollTimeoutRef.current) clearTimeout(rollTimeoutRef.current); if (rollTimeoutRef.current) clearTimeout(rollTimeoutRef.current)
}; }
}, []); }, [])
const rollDice = () => { // Helper that starts the rolling animation and finishes with targetValue if provided
if (isRolling) return; const startRoll = (targetValue = null) => {
if (isRolling) return
setIsRolling(true); setIsRolling(true)
let duration = 0; let duration = 0
const rollInterval = 80; // ms between dice face changes const rollInterval = 80 // ms between dice face changes
const maxDuration = 1500; // total animation time const maxDuration = 1500 // total animation time
const rollAnimation = () => { const rollAnimation = () => {
const randomValue = Math.floor(Math.random() * 6) + 1; const randomValue = Math.floor(Math.random() * 6) + 1
setDiceValue(randomValue); setDiceValue(randomValue)
duration += rollInterval; duration += rollInterval
if (duration < maxDuration) { if (duration < maxDuration) {
// Speed effect: slow down towards the end // Speed effect: slow down towards the end
const nextInterval = rollInterval * (1 + (duration / maxDuration) * 2); const nextInterval = rollInterval * (1 + (duration / maxDuration) * 2)
rollTimeoutRef.current = setTimeout(() => { rollTimeoutRef.current = setTimeout(() => {
animationRef.current = requestAnimationFrame(rollAnimation); animationRef.current = requestAnimationFrame(rollAnimation)
}, nextInterval); }, nextInterval)
} else { } else {
// Final roll // Final roll (use targetValue if provided)
const finalValue = Math.floor(Math.random() * 6) + 1; const finalValue = targetValue != null ? Number(targetValue) : Math.floor(Math.random() * 6) + 1
setDiceValue(finalValue); setDiceValue(finalValue)
setIsRolling(false); setIsRolling(false)
if (onRoll) onRoll(finalValue); if (onRoll) onRoll(finalValue)
}
} }
};
animationRef.current = requestAnimationFrame(rollAnimation); animationRef.current = requestAnimationFrame(rollAnimation)
}; }
// Click to roll randomly
const rollDice = () => {
startRoll(null)
}
// If parent provides a selectedValue, animate to that value
useEffect(() => {
if (selectedValue != null) {
startRoll(Number(selectedValue))
}
}, [selectedValue])
return ( return (
<div <div className={`dice-container ${isRolling ? "rolling" : ""}`} onClick={rollDice}>
className={`dice-container ${isRolling ? 'rolling' : ''}`} <div className="dice">{diceFaces[diceValue - 1]}</div>
onClick={rollDice}
>
<div className="dice">
{diceFaces[diceValue - 1]}
</div>
<style jsx>{` <style jsx>{`
.dice-container { .dice-container {
width: 80px; width: 80px;
@@ -107,11 +136,21 @@ const Dice = ({ onRoll }) => {
} }
@keyframes roll { @keyframes roll {
0% { transform: rotateX(0deg) rotateY(0deg); } 0% {
25% { transform: rotateX(90deg) rotateY(45deg); } transform: rotateX(0deg) rotateY(0deg);
50% { transform: rotateX(180deg) rotateY(90deg); } }
75% { transform: rotateX(270deg) rotateY(135deg); } 25% {
100% { transform: rotateX(360deg) rotateY(180deg); } transform: rotateX(90deg) rotateY(45deg);
}
50% {
transform: rotateX(180deg) rotateY(90deg);
}
75% {
transform: rotateX(270deg) rotateY(135deg);
}
100% {
transform: rotateX(360deg) rotateY(180deg);
}
} }
.dice-dot { .dice-dot {
@@ -123,17 +162,10 @@ const Dice = ({ onRoll }) => {
box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3); box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
} }
/* Positioning dots */ /* removed :nth-child positioning — positions are provided inline from diceDotPositions.js */
.dice-dot:nth-child(1) { top: 50%; left: 50%; transform: translate(-50%, -50%); } /* Center */
.dice-dot:nth-child(2) { top: 20%; left: 20%; } /* Top-left */
.dice-dot:nth-child(3) { bottom: 20%; right: 20%; } /* Bottom-right */
.dice-dot:nth-child(4) { top: 20%; right: 20%; } /* Top-right */
.dice-dot:nth-child(5) { bottom: 20%; left: 20%; } /* Bottom-left */
.dice-dot:nth-child(6) { top: 50%; left: 20%; transform: translateY(-50%); } /* Middle-left */
.dice-dot:nth-child(7) { top: 50%; right: 20%; transform: translateY(-50%); } /* Middle-right */
`}</style> `}</style>
</div> </div>
); )
}; }
export default Dice; export default Dice
@@ -0,0 +1,9 @@
export const dotPositions = {
center: { top: "50%", left: "50%", transform: "translate(-50%, -50%)" },
topLeft: { top: "20%", left: "20%" },
bottomRight: { bottom: "20%", right: "20%" },
topRight: { top: "20%", right: "20%" },
bottomLeft: { bottom: "20%", left: "20%" },
middleLeft: { top: "50%", left: "20%", transform: "translateY(-50%)" },
middleRight: { top: "50%", right: "20%", transform: "translateY(-50%)" },
}