Hustler-Pixelwelt T13: Korrekturen Hochhaus (Fokus, Tastatur, weniger Bewegung, Handy-Tippflächen, Schnee per transform, Pause überall, Seite ohne Leerlauf-Blitz)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0924a8d2ae
commit
5e6698f5e0
14 files changed
+994
-52
No files matched your search
@@ -3,9 +3,12 @@
|
||||
// Raumschilder, Bildschirme, Radar, Briefkasten-Zähler, Tresor-/Keller-Text, den Etagen (Knöpfe, Freigabe und Keller sind Links) und den 19 Hustlern; davor der Schnee.
|
||||
// Alles hängt am Dienst-Zustand: Figur und Zusätze nach Status (figurZustand), graue Raum-Ebene samt Papierschild nur für nicht angeschlossene Such-Räume.
|
||||
// Die Kamera (Rad, Ziehen, Pinch, Tasten) steckt in useKamera. `children` (die Regie-Ebene) liegt in der Bühne über den Figuren, in Weltkoordinaten.
|
||||
// Bedienung: Tastaturfokus (:focus-visible) auf Etage oder Figur außerhalb des Bildes holt sie per Kamera herein (beiFokus, kein Schwenk bei Mausklick); das Sichtfenster ist
|
||||
// per tabindex -1 fokussierbar (ein Klick in die Welt gibt ihm den Fokus, damit + - 0 Esc wirken); die Tippflächen der Figuren wachsen mit dem Zoom (--hk-tipp-*, tippRand);
|
||||
// bei offener Detailansicht (gesperrt) trägt das Sichtfenster hk-verdeckt (Schnee aus).
|
||||
// Kein CSS-Import (die Stile hängt WorkersPage ein: styles/hustler/hochhaus.css, hustler-ui.css, hustler-welt.css), keine Bilder, kein innerHTML.
|
||||
import { forwardRef, useImperativeHandle } from 'react'
|
||||
import { anzeigeName, figurZustand, schilder } from '../../../lib/hustlerSicht.js'
|
||||
import { anzeigeName, figurZustand, schilder, tippRand } from '../../../lib/hustlerSicht.js'
|
||||
import { AUFZUG, ETAGEN, ETAGEN_SPALTE, HAUS_BILD, HUSTLER, RAEUME, SCHILDER } from '../../../lib/hustlerWelt.js'
|
||||
import { FLOOR_TITLES, ROLES, ROOM_TITLES } from '../../../lib/workersRoster.js'
|
||||
import useKamera from './useKamera.js'
|
||||
@@ -20,6 +23,11 @@ const HAUS_RECHTECK = Object.freeze({ left: HAUS_BILD.x, top: HAUS_BILD.y, width
|
||||
|
||||
const PX = (n) => `${n}px`
|
||||
|
||||
// Elemente, die Fokus per Tastatur bekommen können und beim Fokus ins Bild geholt werden (M1)
|
||||
const FOKUS_ZIEL = '.hk-etage, .hk-hustler'
|
||||
// Namensschild einer Figur: so viele Bildschirm-Pixel über dem Knopf sollen beim Heranholen mit sichtbar sein
|
||||
const SCHILD_HOEHE = 34
|
||||
|
||||
// Feste, je Figur verschiedene Startverschiebung der Animation (deterministisch aus dem Index: 7 und 19 sind teilerfremd, 19 Figuren -> 19 Werte)
|
||||
const versatz = (index, schritt = 7, anzahl = 19) => `-${(((index * schritt) % anzahl) / 10).toFixed(1)}s`
|
||||
|
||||
@@ -49,6 +57,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
const grau = new Set(figuren.filter((x) => x.f.raumGrau).map((x) => x.f.raum))
|
||||
|
||||
const { s, x, y } = kam
|
||||
const tipp = tippRand(s)
|
||||
const etikett = Math.min(18, Math.max(8, 15 / s)).toFixed(2)
|
||||
const namePx = Math.min(14, Math.max(4, 12 / s)).toFixed(2)
|
||||
const buehneStil = {
|
||||
@@ -56,6 +65,10 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
imageRendering: s * geraeteDichte() < 1 ? 'auto' : 'pixelated',
|
||||
'--hk-etikett': `${etikett}px`,
|
||||
'--hk-name': `${namePx}px`,
|
||||
// Tippfläche der Figuren: wächst mit dem Zoom auf 40 Bildschirm-Pixel (M8, hustler-welt.css)
|
||||
'--hk-tipp-x': `${tipp.x.toFixed(2)}px`,
|
||||
'--hk-tipp-oben': `${tipp.oben.toFixed(2)}px`,
|
||||
'--hk-tipp-unten': `${tipp.unten.toFixed(2)}px`,
|
||||
}
|
||||
|
||||
const zeige = (id) => () => {
|
||||
@@ -70,10 +83,29 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
if (typeof onOpen === 'function') onOpen(tab)
|
||||
}
|
||||
|
||||
// Fokus per Tastatur (:focus-visible) auf Etage oder Figur außerhalb des sichtbaren Bereichs: Kamera holt sie ins Bild (M1). Kein Schwenk bei Mausklick.
|
||||
const beiFokus = (e) => {
|
||||
if (gesperrt) return
|
||||
const knoten = e && e.target
|
||||
if (!knoten || typeof knoten.closest !== 'function') return
|
||||
const ziel = knoten.closest(FOKUS_ZIEL)
|
||||
if (!ziel) return
|
||||
let tastatur = false
|
||||
try { tastatur = ziel.matches(':focus-visible') } catch { tastatur = false }
|
||||
if (!tastatur) return
|
||||
// Der Browser darf das Sichtfenster nicht selbst verschieben (alte Browser ohne overflow: clip scrollen beim Fokus)
|
||||
const sicht = e.currentTarget
|
||||
if (sicht && (sicht.scrollLeft || sicht.scrollTop)) { sicht.scrollLeft = 0; sicht.scrollTop = 0 }
|
||||
const figur = typeof ziel.matches === 'function' && ziel.matches('.hk-hustler')
|
||||
kam.zeige({ left: ziel.offsetLeft, top: ziel.offsetTop, width: ziel.offsetWidth, height: ziel.offsetHeight }, figur ? SCHILD_HOEHE : 0)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={kam.view}
|
||||
className={`hk-sicht${kam.ziehen ? ' hk-zieht' : ''}${pausiert ? ' hk-steht' : ''}`}
|
||||
className={`hk-sicht${kam.ziehen ? ' hk-zieht' : ''}${pausiert ? ' hk-steht' : ''}${gesperrt ? ' hk-verdeckt' : ''}`}
|
||||
tabIndex={-1}
|
||||
onFocus={beiFokus}
|
||||
onPointerDown={kam.handlers.onPointerDown}
|
||||
onPointerMove={kam.handlers.onPointerMove}
|
||||
onPointerUp={kam.handlers.onPointerUp}
|
||||
@@ -100,6 +132,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
<div className="hk-haus hk-schild-b" aria-hidden="true" style={HAUS_RECHTECK} />
|
||||
<div
|
||||
className="hk-neon"
|
||||
aria-hidden="true"
|
||||
style={{ left: SCHILDER.neon.left, top: SCHILDER.neon.top, width: SCHILDER.neon.w, height: SCHILDER.neon.h, fontSize: PX(SCHILDER.neon.schrift), lineHeight: PX(SCHILDER.neon.schrift) }}
|
||||
>
|
||||
HUSTLER
|
||||
@@ -186,13 +219,13 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
const link = LINK_ETAGEN[e.id]
|
||||
if (link) {
|
||||
return (
|
||||
<a key={e.id} className="hk-etage" href={`/hustler?tab=${link.tab}`} draggable={false} onClick={linkKlick(link.tab)} aria-label={link.label} style={stil}>
|
||||
<a key={e.id} className="hk-etage" data-hk-etage={e.id} href={`/hustler?tab=${link.tab}`} draggable={false} onClick={linkKlick(link.tab)} aria-label={link.label} style={stil}>
|
||||
<span className="hk-etage-name">{link.text}</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<button key={e.id} type="button" className="hk-etage" onClick={() => kam.etage(e.oben, e.unten)} aria-label={`${FLOOR_TITLES[e.id]} heranzoomen`} style={stil}>
|
||||
<button key={e.id} type="button" className="hk-etage" data-hk-etage={e.id} onClick={() => kam.etage(e.oben, e.unten)} aria-label={`${FLOOR_TITLES[e.id]} heranzoomen`} style={stil}>
|
||||
<span className="hk-etage-name">{FLOOR_TITLES[e.id]}</span>
|
||||
</button>
|
||||
)
|
||||
@@ -207,6 +240,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
type="button"
|
||||
className="hk-hustler"
|
||||
data-hk-hustler={id}
|
||||
aria-haspopup="dialog"
|
||||
onClick={zeige(id)}
|
||||
aria-label={f.label}
|
||||
style={{ left: k.left, top: k.top, width: k.w, height: k.h }}
|
||||
|
||||
Reference in new issue
Block a user