Add vote progress bar and related styles to enhance voting feedback
All checks were successful
Publish Docker image / Push Docker image to Docker Hub (push) Successful in 3m21s

This commit is contained in:
Mathias Wagner 2025-05-14 22:10:27 +02:00
parent 8f91e27ca1
commit f3c87878ce
2 changed files with 298 additions and 0 deletions

View File

@ -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

View File

@ -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() {
</div>
</div>
{/* Vote Progress Bar - Only show after voting */}
{hasVoted && (
<div className="vote-progress-container">
<div className="vote-progress-labels">
<span className={`vote-label left ${voteRatio.winner === 'song1' ? 'winning' : ''} ${voteRatio.winner === 'song1' && voteRatio.isLandslide ? 'landslide' : ''}`}>
{battle.song1Votes || 0} Stimmen
{voteRatio.winner === 'song1' && voteRatio.totalVotes > 1 &&
<span className="winner-crown"><FontAwesomeIcon icon={faCrown} /></span>
}
</span>
<span className={`vote-label right ${voteRatio.winner === 'song2' ? 'winning' : ''} ${voteRatio.winner === 'song2' && voteRatio.isLandslide ? 'landslide' : ''}`}>
{battle.song2Votes || 0} Stimmen
{voteRatio.winner === 'song2' && voteRatio.totalVotes > 1 &&
<span className="winner-crown"><FontAwesomeIcon icon={faCrown} /></span>
}
</span>
</div>
<div className={`vote-progress-bar ${voteRatio.winner !== 'tie' ? voteRatio.winner : ''} ${voteRatio.isLandslide ? 'landslide' : ''}`}>
<div
className={`vote-progress-fill song1 ${voteRatio.winner === 'song1' ? 'winning' : ''}`}
style={{ width: `${voteRatio.song1Percent}%` }}
>
{voteRatio.song1Percent > 5 && (
<div className="vote-progress-decoration left"></div>
)}
</div>
<div
className={`vote-progress-divider ${voteRatio.margin <= 10 ? 'close-race' : ''}`}
style={{ left: `${voteRatio.song1Percent}%` }}
></div>
<div
className={`vote-progress-fill song2 ${voteRatio.winner === 'song2' ? 'winning' : ''}`}
style={{ width: `${voteRatio.song2Percent}%` }}
>
{voteRatio.song2Percent > 5 && (
<div className="vote-progress-decoration right"></div>
)}
</div>
</div>
<div className="vote-progress-percentages">
<span className={`vote-percent left ${voteRatio.winner === 'song1' ? 'winning' : ''} ${voteRatio.winner === 'song1' && voteRatio.isLandslide ? 'landslide' : ''}`}>
{voteRatio.song1Percent}%
</span>
<span className={`vote-percent right ${voteRatio.winner === 'song2' ? 'winning' : ''} ${voteRatio.winner === 'song2' && voteRatio.isLandslide ? 'landslide' : ''}`}>
{voteRatio.song2Percent}%
</span>
</div>
</div>
)}
{!hasVoted && (
<div className="voting-actions">
<button