diff --git a/client/src/common/styles/components/voting-screen.sass b/client/src/common/styles/components/voting-screen.sass index 7345a67..e33650b 100644 --- a/client/src/common/styles/components/voting-screen.sass +++ b/client/src/common/styles/components/voting-screen.sass @@ -447,3 +447,230 @@ justify-content: center margin: 2rem 0 animation: pixel-float 3s infinite ease-in-out + +// Vote Progress Bar +.vote-progress-container + width: 100% + margin-top: 1.5rem + margin-bottom: 2rem + padding: 0 0.5rem + animation: fade-in-up 0.5s ease forwards + position: relative + +@keyframes fade-in-up + from + opacity: 0 + transform: translateY(10px) + to + opacity: 1 + transform: translateY(0) + +.vote-progress-labels + display: flex + justify-content: space-between + margin-bottom: 0.5rem + font-size: 0.8rem + font-family: 'Press Start 2P', monospace + + .vote-label + padding: 0.5rem 0.75rem + background-color: rgba(0, 0, 0, 0.7) + border: 2px solid #000 + transform: translateY(0) + transition: transform 0.3s ease + position: relative + z-index: 2 + + &.left + color: $primary + border-left-color: $primary + border-top-color: $primary + border-bottom-color: $primary + box-shadow: -3px 3px 0 rgba($primary, 0.3) + &:hover + transform: translateY(-3px) + box-shadow: -5px 5px 0 rgba($primary, 0.5) + + &.right + color: $accent + border-right-color: $accent + border-top-color: $accent + border-bottom-color: $accent + box-shadow: 3px 3px 0 rgba($accent, 0.3) + &:hover + transform: translateY(-3px) + box-shadow: 5px 5px 0 rgba($accent, 0.5) + + &.winning + background-color: rgba(#000, 0.8) + border-width: 3px + transform: translateY(-3px) + position: relative + z-index: 3 + animation: winner-pulse 1.5s infinite alternate + + &.left + box-shadow: -5px 5px 0 rgba($primary, 0.7), 0 0 15px rgba($primary, 0.7) + + &.right + box-shadow: 5px 5px 0 rgba($accent, 0.7), 0 0 15px rgba($accent, 0.7) + + &.landslide + transform: translateY(-5px) + font-weight: bold + animation: winner-landslide-pulse 1s infinite alternate + + &.left + box-shadow: -6px 6px 0 rgba($primary, 0.7), 0 0 25px rgba($primary, 0.9) + + &.right + box-shadow: 6px 6px 0 rgba($accent, 0.7), 0 0 25px rgba($accent, 0.9) + +@keyframes winner-landslide-pulse + from + box-shadow: -6px 6px 0 rgba($primary, 0.7), 0 0 25px rgba($primary, 0.9) + to + box-shadow: -6px 6px 0 rgba($primary, 0.7), 0 0 40px rgba($primary, 1) + +.vote-progress-bar + height: 36px + background-color: #222 + border: 4px solid #000 + position: relative + overflow: hidden + display: flex + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3) + + .vote-progress-fill + height: 100% + transition: width 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) + position: relative + min-width: 0 + + &.song1 + background: linear-gradient(to right, darken($primary, 20%), $primary) + box-shadow: 0 8px 24px rgba($primary, 0.3), 0 0 10px rgba($primary, 0.2) + animation: pulse-song1 2s infinite alternate + + &.song2 + background: linear-gradient(to left, darken($accent, 20%), $accent) + box-shadow: 0 8px 24px rgba($accent, 0.3), 0 0 10px rgba($accent, 0.2) + animation: pulse-song2 2s infinite alternate + + .vote-progress-decoration + position: absolute + top: 0 + bottom: 0 + width: 20px + background-size: 4px 4px + opacity: 0.6 + + &.left + right: 10px + background-image: linear-gradient(45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent) + animation: move-stripes-left 20s linear infinite + + &.right + left: 10px + background-image: linear-gradient(-45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent) + animation: move-stripes-right 20s linear infinite + + &.landslide + border-width: 5px + animation: landslide-border-pulse 1.5s infinite alternate + + &.song1 + border-color: lighten($primary, 20%) + + &.song2 + border-color: lighten($accent, 20%) + + .vote-progress-divider + position: absolute + top: -8px + bottom: -8px + width: 6px + background-color: #fff + transform: translateX(-50%) + z-index: 10 + box-shadow: 0 0 15px rgba(255, 255, 255, 0.7), 0 0 5px rgba(0, 0, 0, 0.5) + transition: left 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) + &:before, &:after + content: '' + position: absolute + left: 50% + width: 12px + height: 12px + background-color: #fff + transform: translateX(-50%) rotate(45deg) + box-shadow: 0 0 10px rgba(255, 255, 255, 0.7), 0 0 3px rgba(0, 0, 0, 0.5) + + &:before + top: 0 + + &:after + bottom: 0 + +.vote-progress-percentages + display: flex + justify-content: space-between + margin-top: 0.75rem + font-family: 'Press Start 2P', monospace + font-size: 1rem + + .vote-percent + font-weight: bold + text-shadow: 2px 2px 0 #000 + padding: 0.4rem 0.75rem + background-color: rgba(0, 0, 0, 0.7) + border: 2px solid #000 + letter-spacing: 1px + position: relative + + &.left + color: $primary + border-left-color: $primary + border-bottom-color: $primary + transform: skew(-10deg) + box-shadow: -3px 3px 0 rgba($primary, 0.4) + + &.right + color: $accent + border-right-color: $accent + border-bottom-color: $accent + transform: skew(10deg) + box-shadow: 3px 3px 0 rgba($accent, 0.4) + + &.winning + font-size: 1.2rem + font-weight: bolder + animation: winner-text-pulse 2s infinite alternate + + &.left + box-shadow: -5px 5px 0 rgba($primary, 0.5), 0 0 10px rgba($primary, 0.5) + + &.right + box-shadow: 5px 5px 0 rgba($accent, 0.5), 0 0 10px rgba($accent, 0.5) + + &.landslide + font-size: 1.3rem + animation: landslide-text-pulse 1s infinite alternate + + &.left + box-shadow: -6px 6px 0 rgba($primary, 0.6), 0 0 15px rgba($primary, 0.6) + + &.right + box-shadow: 6px 6px 0 rgba($accent, 0.6), 0 0 15px rgba($accent, 0.6) + +@keyframes landslide-text-pulse + from + text-shadow: 2px 2px 0 #000, 0 0 10px currentColor + to + text-shadow: 2px 2px 0 #000, 0 0 25px currentColor, 0 0 5px #fff + +// Bye container for automatic advances +.bye-container + display: flex + justify-content: center + margin: 2rem 0 + animation: pixel-float 3s infinite ease-in-out diff --git a/client/src/components/VotingScreen.jsx b/client/src/components/VotingScreen.jsx index 7995391..52fb814 100644 --- a/client/src/components/VotingScreen.jsx +++ b/client/src/components/VotingScreen.jsx @@ -37,6 +37,27 @@ function VotingScreen() { return 'Vorrunde'; }, [lobby, battle]); + // Berechne das Verhältnis der Stimmen für den Fortschrittsbalken + const voteRatio = useMemo(() => { + if (!battle || !hasVoted) return { song1Percent: 50, song2Percent: 50 }; + + const totalVotes = (battle.song1Votes || 0) + (battle.song2Votes || 0); + + if (totalVotes === 0) return { song1Percent: 50, song2Percent: 50 }; + + const song1Percent = Math.round((battle.song1Votes / totalVotes) * 100); + const song2Percent = 100 - song1Percent; + + // Determine winner (for styling purposes) + const winner = song1Percent > song2Percent ? 'song1' : song1Percent < song2Percent ? 'song2' : 'tie'; + + // Calculate margin of victory for animation intensity + const margin = Math.abs(song1Percent - song2Percent); + const isLandslide = margin >= 30; + + return { song1Percent, song2Percent, winner, totalVotes, isLandslide, margin }; + }, [battle, hasVoted]); + // Prüfe, ob der Spieler bereits abgestimmt hat useEffect(() => { if (battle && battle.votes && currentPlayer) { @@ -296,6 +317,56 @@ function VotingScreen() { + {/* Vote Progress Bar - Only show after voting */} + {hasVoted && ( +
+
+ + {battle.song1Votes || 0} Stimmen + {voteRatio.winner === 'song1' && voteRatio.totalVotes > 1 && + + } + + + {battle.song2Votes || 0} Stimmen + {voteRatio.winner === 'song2' && voteRatio.totalVotes > 1 && + + } + +
+
+
+ {voteRatio.song1Percent > 5 && ( +
+ )} +
+
+
+ {voteRatio.song2Percent > 5 && ( +
+ )} +
+
+
+ + {voteRatio.song1Percent}% + + + {voteRatio.song2Percent}% + +
+
+ )} + {!hasVoted && (