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:
ClaudeandClaude Opus 5.5 committed 2026-10-22 09:47:57 +00:00
1 parent 0924a8d2ae
commit 5e6698f5e0
14 files changed
+994 -52

No files matched your search

+39 -14
View File
@@ -1,21 +1,43 @@
import { useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import HochhausKopf from './HochhausKopf'
import HochhausWelt from './HochhausWelt'
import HustlerDialog from './HustlerDialog'
import RegieEbene from './RegieEbene'
// Von welchem Bildschirm man zurückkommt -> Etage, über die man gegangen war (Freigabe-Pult über die Etage „freigabe“, Keller über „keller“)
const ETAGE_NACH_TAB = Object.freeze({ freigabe: 'freigabe', botleads: 'keller' })
// Überschrift der Seite, nur für Screenreader (Pult und Keller haben ihre eigene sichtbare Überschrift)
function Ueberschrift() {
return <h1 className="hk-nur-sr">Hustler – Hochhaus</h1>
}
// Bildschirm „Hochhaus“: Spielwelt mit Regie-Ebene (T5, T8), Kopfzeile (T6) und Detailansicht eines Hustlers (T7). Zustände wie bisher im WorkersBoard.
// Reine Darstellung ohne Auth-Bezug: isAdmin, der Feed (feed = { state, error, loaded, refresh }), die Hustler-Tabelle (workers), die Regie-Anbindung
// (regieRef, die Seite ruft regieRef.current.push(events) bei neuen Ereignissen) und der Bildschirmwechsel (onOpen(tab)) kommen von der Seite.
// (regieRef, die Seite ruft regieRef.current.push(events) bei neuen Ereignissen), der Bildschirmwechsel (onOpen(tab)) und vonTab (der Bildschirm, von dem man
// zurückkommt: 'freigabe' | 'botleads' | null) kommen von der Seite. Beim Zurückkommen bekommt die Etage, über die man gegangen war, den Fokus (M2).
// Die Kopfzeile steht im DOM vor der Welt, damit die Tab-Reihenfolge oben beginnt (M7); über der Welt liegt sie per z-index.
// Kein CSS-Import und keine Bilder: die Stile hängt WorkersPage ein (Regeln dieser Datei in styles/hustler/hustler-ui.css).
export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen }) {
export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen, vonTab = null }) {
const wurzelRef = useRef(null)
const weltRef = useRef(null)
const [auswahl, setAuswahl] = useState(null) // workerId der offenen Detailansicht (null = keine)
const ready = feed.loaded && Boolean(feed.state)
// Rückweg aus Pult oder Keller: Fokus auf die Etage, über die man ging (erst wenn die Welt steht; die Kamera holt sie bei Tastaturfokus ins Bild)
useEffect(() => {
if (!ready || !vonTab || !Object.hasOwn(ETAGE_NACH_TAB, vonTab)) return
const wurzel = wurzelRef.current
if (!wurzel || typeof wurzel.querySelector !== 'function') return
const etage = wurzel.querySelector(`[data-hk-etage="${ETAGE_NACH_TAB[vonTab]}"]`)
if (etage && typeof etage.focus === 'function') etage.focus({ preventScroll: true })
}, [ready, vonTab])
// Dienst noch nie erreicht: Pixel-Leerzustand mit Fehlertext und „Neu laden“
if (!ready && feed.error) {
return (
<div className="hk-hochhaus">
<div className="hk-hochhaus" ref={wurzelRef}>
<Ueberschrift />
<div className="hk-leer">
<strong className="hk-leer-titel">Der Hustler-Dienst ist noch nicht erreichbar</strong>
<p className="hk-leer-text">{feed.error}</p>
@@ -26,7 +48,8 @@ export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen
}
if (!ready) {
return (
<div className="hk-hochhaus">
<div className="hk-hochhaus" ref={wurzelRef}>
<Ueberschrift />
<div className="hk-laden" role="status">Wird geladen …</div>
</div>
)
@@ -35,7 +58,16 @@ export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen
// Die Regie-Ebene liegt in der skalierten Bühne (Weltkoordinaten); ihr Handle ist regieRef der Seite, die neue Ereignisse hineinreicht.
// Solange die Detailansicht offen ist, ruhen Rad, Ziehen und Tasten der Kamera (gesperrt).
return (
<div className="hk-hochhaus">
<div className="hk-hochhaus" ref={wurzelRef}>
<Ueberschrift />
<HochhausKopf
api={api}
isAdmin={isAdmin}
state={feed.state}
error={feed.error}
refresh={feed.refresh}
onGanz={() => { if (weltRef.current) weltRef.current.ganz() }}
/>
<HochhausWelt
ref={weltRef}
state={feed.state}
@@ -46,19 +78,12 @@ export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen
>
<RegieEbene ref={regieRef} />
</HochhausWelt>
<HochhausKopf
api={api}
isAdmin={isAdmin}
state={feed.state}
error={feed.error}
refresh={feed.refresh}
onGanz={() => { if (weltRef.current) weltRef.current.ganz() }}
/>
{auswahl !== null && (
<HustlerDialog
api={api}
workerId={auswahl}
workers={workers}
paused={feed.state?.paused === true}
onClose={() => setAuswahl(null)}
onWechsel={setAuswahl}
/>
@@ -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 }}
@@ -8,7 +8,8 @@
// Esc schließt, ←/→ blättern in Roster-Reihenfolge, ein Klick auf den Hintergrund schließt erst 400 ms nach dem Öffnen (Geister-Klick nach
// dem Antippen der Figur). Beim Schließen geht der Fokus zurück auf das Element, das beim Öffnen den Fokus hatte (wenn es noch im Dokument
// steht), sonst auf die Figur des zuletzt gezeigten Hustlers ([data-hk-hustler="<id>"], die setzt die Welt).
import { useEffect, useRef } from 'react'
// Blättern wird angesagt (aria-live polite: Name, Status, Nummer), nicht aber das Öffnen. Angehalten (paused): die Animationen im Dialog stehen (hk-steht).
import { useEffect, useRef, useState } from 'react'
import { Link } from 'react-router-dom'
import { useWorkerTrace } from '../../../hooks/useWorkersFeed'
import { blaettern, detailModell } from '../../../lib/hustlerSicht'
@@ -39,9 +40,12 @@ function figurStelle(szene) {
return DETAIL.figur.buero
}
// Ansage nach dem Blättern: „Lea, Arbeitet, 9 von 19“
const ansageVon = (m) => `${m.name}, ${m.statusText}, ${m.nummer.replace(' / ', ' von ')}`
// Props: api (Dienst-API für die Spur), workerId (null = nichts anzeigen), workers (Hustler-Tabelle der Seite, Zustandseinträge nach workerId),
// onClose(), onWechsel(nextId) (Blättern; die Seite setzt die neue Id)
export default function HustlerDialog({ api, workerId, workers, onClose, onWechsel }) {
// paused (Dienst angehalten: Animationen stehen), onClose(), onWechsel(nextId) (Blättern; die Seite setzt die neue Id)
export default function HustlerDialog({ api, workerId, workers, paused = false, onClose, onWechsel }) {
const { trace, loading, error } = useWorkerTrace(api, workerId)
const ersterRef = useRef(null) // Knopf „Schließen“ (Fokus beim Öffnen, Anfang der Fokusfalle)
const letzterRef = useRef(null) // Knopf „Nächster Hustler“ (Ende der Fokusfalle)
@@ -50,6 +54,7 @@ export default function HustlerDialog({ api, workerId, workers, onClose, onWechs
const geoeffnetRef = useRef(0)
const openerRef = useRef(null)
const idRef = useRef(workerId)
const [ansage, setAnsage] = useState('') // Text der Live-Region: leer beim Öffnen, nach dem Blättern der neue Hustler
const offen = Boolean(workerId)
if (workerId) idRef.current = workerId
@@ -59,6 +64,7 @@ export default function HustlerDialog({ api, workerId, workers, onClose, onWechs
const aktiv = typeof document !== 'undefined' ? document.activeElement : null
openerRef.current = aktiv && aktiv !== document.body ? aktiv : null
geoeffnetRef.current = Date.now()
setAnsage('')
const erster = ersterRef.current
if (erster && typeof erster.focus === 'function') erster.focus({ preventScroll: true })
return () => {
@@ -78,7 +84,13 @@ export default function HustlerDialog({ api, workerId, workers, onClose, onWechs
const m = detailModell(workerId, eintrag, trace, Date.now())
const stelle = figurStelle(m.szene)
const o = m.overlays
const wechsel = (schritt) => onWechsel(blaettern(workerId, schritt))
const wechsel = (schritt) => {
const naechster = blaettern(workerId, schritt)
// Ansage des Ziels (Name, Status, Nummer hängen nicht an der Spur) als Momentaufnahme: spätere Statuswechsel melden sich nicht von selbst
const ziel = isRecord(workers) && Object.hasOwn(workers, naechster) ? workers[naechster] : null
setAnsage(ansageVon(detailModell(naechster, ziel, null, Date.now())))
onWechsel(naechster)
}
// Hintergrund: schließt erst nach der Sperrzeit
const hintergrund = () => {
@@ -115,7 +127,7 @@ export default function HustlerDialog({ api, workerId, workers, onClose, onWechs
}
return (
<div className="hk-dialog-ueber" onKeyDown={taste}>
<div className={paused === true ? 'hk-dialog-ueber hk-steht' : 'hk-dialog-ueber'} onKeyDown={taste}>
<button type="button" tabIndex={-1} className="hk-dialog-hinten" aria-label="Detailansicht schließen" ref={hintenRef} onClick={hintergrund} />
<section role="dialog" aria-modal="true" aria-label={`Hustler ${m.name}`} tabIndex={-1} className="hk-panel hk-dialog" ref={sectionRef}>
<div className={m.szene} aria-hidden="true">
@@ -132,6 +144,7 @@ export default function HustlerDialog({ api, workerId, workers, onClose, onWechs
</span>
)}
</div>
<div className="hk-nur-sr" aria-live="polite" aria-atomic="true">{ansage}</div>
<div className="hk-dialog-daten">
<div className="hk-dialog-kopf">
<div>
+49 -4
View File
@@ -1,10 +1,12 @@
// Kamera der Hochhaus-Welt (Hustler-Pixelwelt T5, Spec E1/E8/E14): Port von logik.js der Zeichenfläche als Hook.
// Mausrad zoomt um den Mauspunkt, Ziehen verschiebt, zwei Finger zoomen (Pinch), Doppelklick/Doppeltipp zeigt das ganze Haus,
// Tasten +/=/-/0/Esc, Etagen heranzoomen. Gleiten 0,55 s (aus bei „weniger Bewegung“). Nach einem Zug kein versehentlicher Klick (nachZug()).
// Tasten +/=/-/0/Esc, Etagen heranzoomen, ein Element ins Bild holen (zeige, für den Tastaturfokus). Gleiten 0,55 s (aus bei „weniger Bewegung“: dann springt die Kamera).
// Nach einem Zug kein versehentlicher Klick (nachZug()). Ein Zeigerdruck in die Welt gibt dem Sichtfenster den Fokus.
// Gerechnet wird nur mit den reinen Kamera-Funktionen aus lib/hustlerSicht.js; hier steht die Zeiger- und Zustandsverwaltung.
// Die Kamera selbst (baueKamera) ist reines JS ohne React und bekommt nur die Meldung „Zustand hat sich geändert“.
import { useEffect, useRef, useState } from 'react'
import { SICHT_STANDARD, kameraEtage, kameraGrenzen, kameraPassend, kameraZoomAuf } from '../../../lib/hustlerSicht.js'
import { SICHT_STANDARD, kameraEtage, kameraGrenzen, kameraPassend, kameraZeige, kameraZoomAuf } from '../../../lib/hustlerSicht.js'
import { WELT } from '../../../lib/hustlerWelt.js'
// Gleiten: die CSS-Klasse hk-gleitet dauert 0,55 s, der Merker bleibt etwas länger (Ende der Bewegung abwarten)
export const GLEITEN_MS = 600
@@ -14,6 +16,10 @@ const ZUG_SCHWELLE_MAUS = 5
const ZUG_SCHWELLE_TOUCH = 10
// Beschreibt Elemente, auf denen ein Doppelklick/-tipp nicht „ganzes Haus“ bedeutet
const BEDIENBAR = '.hk-etage, .hk-hustler'
// Ein Zeigerdruck auf diesen Elementen fokussiert sich selbst; überall sonst bekommt das Sichtfenster den Fokus (damit + - 0 Esc wirken)
const SELBST_FOKUS = 'button, a[href], input, select, textarea, [tabindex]'
// Obergrenze für die von der Kopfzeile verdeckte Höhe (Anteil der Fensterhöhe), falls viele Meldungen darunter stehen
const KOPF_MAX_ANTEIL = 0.5
const jetzt = () => Date.now()
@@ -92,8 +98,17 @@ export function baueKamera(melde) {
if (m.st.ziehen) setze({ ziehen: false })
}
// Ein Druck in die Welt gibt dem Sichtfenster den Fokus (tabindex -1), sonst erreichen + - 0 Esc die Kamera nach einem Klick nicht mehr
const fokusAufSicht = (e) => {
const ziel = e && e.target
if (ziel && typeof ziel.closest === 'function' && ziel.closest(SELBST_FOKUS)) return
if (m.el && typeof m.el.focus === 'function') m.el.focus({ preventScroll: true })
}
const unten = (e) => {
if (m.gesperrt || m.st.gleiten || !m.st.bereit) return
if (m.gesperrt) return
fokusAufSicht(e)
if (m.st.gleiten || !m.st.bereit) return
if (e.pointerType === 'mouse' && (e.button !== 0 || e.ctrlKey)) return
if (e.pointerType === 'mouse') m.zeiger.clear() // es gibt nur eine Maus: ein verwaister Eintrag darf keinen Pinch auslösen
m.zeiger.set(e.pointerId, { x: e.clientX, y: e.clientY })
@@ -186,6 +201,34 @@ export function baueKamera(melde) {
gleite(kameraEtage(oben, untenRand, groesse()))
}
// Höhe, die die Kopfzeile oben im Sichtfenster verdeckt: Unterkante von .hk-kopf (Geschwister des Sichtfensters), sonst die feste Höhe der Welt-Daten
const kopfHoehe = () => {
const g = groesse()
let h = WELT.hud
try {
const wurzel = m.el && m.el.parentElement
const kopf = wurzel && typeof wurzel.querySelector === 'function' ? wurzel.querySelector('.hk-kopf') : null
if (kopf) {
const unterkante = kopf.getBoundingClientRect().bottom - m.el.getBoundingClientRect().top
if (Number.isFinite(unterkante)) h = unterkante
}
} catch { /* Messung nicht möglich: feste Höhe */ }
return Math.min(g.h * KOPF_MAX_ANTEIL, Math.max(0, h))
}
/**
* Holt ein Rechteck der Welt (left, top, width, height) ins Bild, wenn es nicht ganz sichtbar ist (Tastaturfokus, M1): Kamera gleitet, bei „weniger Bewegung“
* springt sie. `obenExtra` = Bildschirm-Pixel über dem Rechteck, die mit sichtbar sein sollen (Namensschild). Gesperrt: nichts.
*/
const zeige = (rechteck, obenExtra = 0) => {
if (m.gesperrt || !m.st.bereit || !rechteck) return
const extra = Number.isFinite(obenExtra) && obenExtra > 0 && m.st.s > 0 ? obenExtra / m.st.s : 0
const ziel = kameraZeige(m.st, { ...rechteck, top: rechteck.top - extra, height: rechteck.height + extra }, groesse(), { oben: kopfHoehe() })
if (!ziel) return
m.selbst = true
gleite(ziel)
}
// Mausrad (passive: false, sonst scrollt die Seite mit) und Größenänderung am Sichtfenster
const verbinden = () => {
if (!m.el || m.verbunden) return
@@ -229,6 +272,7 @@ export function baueKamera(melde) {
loesen,
ganz,
etage,
zeige,
nachZug,
handlers: {
onPointerDown: unten,
@@ -243,7 +287,7 @@ export function baueKamera(melde) {
/**
* Hook für die Welt. `gesperrt` = Detailansicht offen: Rad, Ziehen und Tasten wirken dann nicht.
* Liefert { view (Ref-Callback für das Sichtfenster), s, x, y, bereit, gleiten, ziehen, handlers, ganz(), etage(oben, unten), nachZug() }.
* Liefert { view (Ref-Callback für das Sichtfenster), s, x, y, bereit, gleiten, ziehen, handlers, ganz(), etage(oben, unten), zeige(rechteck, obenExtra), nachZug() }.
* s/x/y: Welt-Pixel mal s plus Verschiebung x/y (transform: translate(x, y) scale(s)).
*/
export default function useKamera({ gesperrt = false } = {}) {
@@ -269,6 +313,7 @@ export default function useKamera({ gesperrt = false } = {}) {
handlers: kam.handlers,
ganz: kam.ganz,
etage: kam.etage,
zeige: kam.zeige,
nachZug: kam.nachZug,
}
}