perf, rework(NameLayer): render player names + status icons directly to main canvas

Replace the DOM-based NameLayer overlay (per-player elements + icon <img>s)
with direct CanvasRenderingContext2D drawing in renderLayer(). This removes
layout/style churn and reduces per-frame overhead when many players are visible.

Key changes:
- Draw name, flag, troops, and status icons on the provided main canvas context
  (no private canvas, no fixed-position container div).
- Add shared state caching (200ms): first-place player id, my transitive targets,
  nuking players, nukes targeting me (for red nuke), dark-mode + emoji settings.
- Add per-player render caching for formatted troops + measured text widths,
  and invalidate metrics on font changes to avoid zoom jitter.
- Re-implement icon rendering on-canvas:
  - crown (first place)
  - traitor icon with time-based alpha flashing ramping in the last 15s
  - disconnected icon
  - alliance progress icon via clip-fill (plus “?” overlay if extension requested)
  - alliance request / embargo icons selecting black/white variants in dark mode
  - nuke icon (red if targeting local player, else white)
  - outgoing emoji (to AllPlayers / me) rendered as text
  - target reticle overlay for transitive targets
- Add image caching for icons/flags and custom-flag mask SVGs to avoid recreating Image objects.
- Restore custom flag support for NameLayer: rasterize `!…` flags via mask compositing into an
  offscreen canvas and cache per flag+size (animated colors re-render at a capped rate).
- CustomFlag: allow rendering without cosmetics metadata; align rainbow durations with CSS.
This commit is contained in:
scamiv
2026-02-06 00:25:45 +01:00
parent 8cc6c2c2aa
commit 818477129c
2 changed files with 714 additions and 483 deletions
File diff suppressed because it is too large Load Diff
+2 -7
View File
@@ -1,8 +1,8 @@
import { Cosmetics } from "./CosmeticSchemas";
const ANIMATION_DURATIONS: Record<string, number> = {
rainbow: 4000,
"bright-rainbow": 4000,
rainbow: 7000,
"bright-rainbow": 7000,
"copper-glow": 3000,
"silver-glow": 3000,
"gold-glow": 3000,
@@ -18,11 +18,6 @@ export function renderPlayerFlag(
target: HTMLElement,
cosmetics: Cosmetics | undefined = undefined,
) {
if (cosmetics === undefined) {
console.warn("No cosmetics provided for flag", flag);
return;
}
if (!flag.startsWith("!")) return;
const code = flag.slice("!".length);