3.8 KiB
3.8 KiB
Header Text Player Meter — Design
Summary
Add a gradient fill meter to the "ROOM CODE:" header text that visually represents lobby fill (playerCount / maxPlayers). The fill sweeps left-to-right from the configured header color (pink) to white. A pulse/glow fires when the lobby is full.
Also: revert the player list checkbox to unchecked by default.
Requirements
- Smooth CSS gradient across the header text, not per-character or per-word.
- Fill percentage =
playerCount / maxPlayers, clamped to[0, 1]. - 0 players → 100% pink. All players → 100% white.
- Gradient edge animates smoothly (~400ms ease) when player count changes.
- Brief pulse/glow animation when fill reaches 100%.
- Existing header settings (text, color, size, offset) continue to work. The configured header color becomes the "unfilled" color; white is always the "filled" color.
- Player list disabled by default.
Approach
CSS background-clip: text with a dynamic linear-gradient.
CSS Changes (optimized-controls.html)
Replace the static #header color with gradient-compatible styles:
#header {
/* existing position, font, letter-spacing, opacity, transition stay */
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
/* drop-shadow replaces text-shadow (incompatible with transparent text) */
filter: drop-shadow(3px 3px 8px rgba(0, 0, 0, 0.8));
text-shadow: none;
}
New keyframes for the full-lobby pulse:
@keyframes meter-full-pulse {
0% { filter: drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); transform: scale(1) translateY(var(--header-offset)); }
50% { filter: drop-shadow(0 0 20px rgba(255,255,255,0.6)) drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); transform: scale(1.05) translateY(var(--header-offset)); }
100% { filter: drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); transform: scale(1) translateY(var(--header-offset)); }
}
#header.meter-full-pulse {
animation: meter-full-pulse 0.6s ease-out;
}
JS Changes (js/room-code-display.js)
- Track meter state — new private fields:
#meterFill(current 0–1),#meterTarget(target 0–1),#meterRafId. #applySettings()— replaceheader.style.color = headerColorwithheader.style.background = linear-gradient(...)using current#meterFilland the configured header color.update(ctx)— compute new target fromctx.playerCount / ctx.maxPlayers. If different from current target, call#animateMeterTo(newTarget).#animateMeterTo(target)—requestAnimationFrameloop that interpolates#meterFilltoward target over ~400ms with ease-out. Each frame calls#applyMeterGradient().#applyMeterGradient()— setsheader.style.backgroundtolinear-gradient(to right, #fff 0%, #fff ${pct}%, ${headerColor} ${pct}%, ${headerColor} 100%)plus re-appliesbackground-clipproperties.#triggerFullPulse()— addsmeter-full-pulseclass, removes afteranimationendevent.deactivate()— cancels RAF, resets#meterFillto 0.
HTML Change (optimized-controls.html)
Remove checked from <input type="checkbox" id="player-list-enabled" checked>.
Data Flow
WebSocket event (lobby.player-joined / player-count-updated)
→ OverlayManager updates context.playerCount
→ OverlayManager calls RoomCodeDisplay.update(ctx)
→ RoomCodeDisplay computes target = playerCount / maxPlayers
→ #animateMeterTo(target) runs RAF interpolation
→ When target reaches 1.0, #triggerFullPulse() fires
Edge Cases
maxPlayersis 0 or missing → fill stays at 0%.playerCount > maxPlayers→ clamp to 100%.- Rapid successive joins → each new target interrupts the current animation, smoothly redirecting.
- Lobby reset (new game) →
deactivate()resets fill to 0; nextactivate()starts fresh.