Files
fuesse-sexy/public/js/app.js
Guido b95464670a Mobile-Fixes, Cover-Rendering, aufrechte Objekte, Fuss-Favicon
- Rendering von SVG-viewBox auf sauberes DOM-Modell (div#world > img) umgestellt:
  behebt den Pinch-Fokus-Bug ("zoomt in die Ecke") auf Touch
- Szene fuellt jetzt bildschirmfuellend (Cover) mit eigener Cover-Klemmung statt
  Letterbox-Balken (v.a. mobil im Hochformat)
- Tippen ueber Pointer-Events statt click (funktioniert auf Touch, wo panzoom das
  synthetische click unterdrueckt); Mehrfinger/Pinch zaehlt nie als Tipp
- touch-action:none aufs Bild -> fluessiges Pinch-Zoom
- Objekte aufrecht (leichte Neigung) statt zufaellig gekippt + dezenter Tiefen-Verlauf
  -> wirkt wie eine Szene statt "Haufen"
- Favicon: Fuss-Emoji

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 18:50:44 +02:00

89 lines
2.6 KiB
JavaScript

// App-Flow: beim Laden sofort eine zufaellige Szene holen (kein Start-Screen, keine
// Auswahl). Alle 3 Fuesse finden -> Gewinn-Overlay -> "Weiter" laedt die naechste.
import { Scene } from './scene.js';
import { Legend } from './legend.js';
const stage = document.getElementById('stage');
const world = document.getElementById('world');
const sceneImg = document.getElementById('scene-img');
const loading = document.getElementById('loading');
const winOverlay = document.getElementById('win-overlay');
const toast = document.getElementById('toast');
const scene = new Scene(stage, world, sceneImg);
const legend = new Legend(
document.getElementById('legend-items'),
document.getElementById('legend-counter')
);
let current = null; // aktuelles Szenen-Payload
let busy = false;
function showLoading(on) {
loading.classList.toggle('hidden', !on);
}
function showToast(message) {
toast.textContent = message;
toast.classList.add('show');
clearTimeout(showToast._t);
showToast._t = setTimeout(() => toast.classList.remove('show'), 900);
}
async function loadScene() {
showLoading(true);
winOverlay.classList.add('hidden');
try {
const res = await fetch('/api/scene');
if (!res.ok) throw new Error('Szene konnte nicht geladen werden.');
current = await res.json();
await scene.render(current);
scene.resetView();
legend.render(current.targets);
document.getElementById('category-name').textContent = current.category || '';
} catch (err) {
console.error(err);
showToast('Ups — Szene konnte nicht geladen werden.');
} finally {
showLoading(false);
}
}
async function handleTap({ x, y }) {
if (busy || !current) return;
busy = true;
try {
const res = await fetch(`/api/scene/${current.sceneId}/guess`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ x, y }),
});
const data = await res.json();
if (data.hit) {
const isNew = legend.markFound(data.hit);
scene.pingAt(x, y, true);
if (isNew && legend.allFound()) {
setTimeout(() => winOverlay.classList.remove('hidden'), 400);
}
} else {
scene.pingAt(x, y, false);
}
} catch (err) {
console.error(err);
} finally {
busy = false;
}
}
scene.onTap(handleTap);
// Steuerung
document.getElementById('zoom-in').addEventListener('click', () => scene.zoomIn());
document.getElementById('zoom-out').addEventListener('click', () => scene.zoomOut());
document.getElementById('reset-view').addEventListener('click', () => scene.resetView());
document.getElementById('next-scene').addEventListener('click', loadScene);
// Los geht's — sofort.
loadScene();