Files

74 lines
5.3 KiB
Markdown

---
name: web-3d-graphics
description: Add real 3D to web pages with Three.js, hosted locally.
version: 1.0
tags:
- threejs
- webgl
- 3d
- frontend
---
# Web-3D-Grafiken (Three.js, lokal gehostet)
## Wann
Nutzer will "richtige 3D-Grafiken" auf einer Webseite (VPS, netcup). Recurring für diesen Nutzer (TicTacToe 3D, PersoEnt-Dashboards usw.).
## Kernsätze (alle VERIFIZIERT an tictactoe.perfekt-ohne-en.de)
### 1. Abhängigkeit lokal hosten — KEIN CDN
- DSGVO-sauber + keine Drittanbieter-Abhängigkeit: `three.js` in das Project-`lib/`-Verzeichnis laden.
- **UMD-Version (global `THREE`) für einfaches `<script src="lib/three.js">`:** nutze **r128** von cdnjs:
`curl -sL -o lib/three.js https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js`
- **NICHT unpkg r160** — das ist ESM-only (`build/three.min.js` existiert dort nicht), wirft Deprecation-Warnungen und ist nicht global verfügbar. r128 ist ein Ordner, global `THREE`.
- nach `curl`: `grep -c REVISION lib/three.js` → `1` bestätigt globale UMD; Syntax parsen mit `node -e "require(...)"`.
### 2. CRITICAL — Geometrie hat KEINE `.rotation`
- `new THREE.BoxGeometry(...)` ist eine `BufferGeometry`: Zugriff `geometry.rotation.z = ...` wirft
`TypeError: Cannot set properties of undefined (setting 'z')`.
- **Fix:** Rotations-Methode direkt auf der Geometrie: `geom.rotateZ(Math.PI/4)` / `rotateY` / `rotateX`.
- Nur Meshes/Groups (Object3D) haben `.position`/`.rotation` — Geometrien nicht.
- Diagnose: der Fehler gibt keine Dateizeilen in der Konsole an, nur den Stack im eigenen `browser_console`-Aufruf; teste Geometrie-Erzeugung isoliert (Material/Box/Torus einzeln) um die fehlerhafte Zeile zu isolieren.
### 3. Standard-Setup (bewährt)
- `renderer = new THREE.WebGLRenderer({canvas, alpha:true, antialias:true}); renderer.setPixelRatio(min(devicePixelRatio,2))`
- `camera = PerspectiveCamera(40, w/h, 0.1, 100)`; z.B. `position(0,2.6,5.2)`, `lookAt(0,0,0)`
- Licht: `AmbientLight(0xffffff,0.7)` + `DirectionalLight(0xffffff,0.9)` bei `(3,5,4)`
- 3D-Marker-Muster:
- **X** = `Group` mit 2 gekreuzten `BoxGeometry`, je `rotateZ(±PI/4)`
- **O** = `TorusGeometry(0.42,0.14,16,40)` mit `rotation.x=PI/2`
- Materialien `MeshPhysicalMaterial({metalness,roughness})` für Glanz
- Animation: `requestAnimationFrame`-Loop.
- Wachsen/Platzieren: Scale von 0.01 → 1 in Tween (ease-out cubic), danach Spin-Rotation um eigene Achse
### 4. CRITICAL — Interaktion: NICHT per Canvas-Klick-Mapping, sondern 2D-Overlay-Grid
Der Nutzer verlor, weil er "blind klickte und die Position nicht traf". **Verwirf den Ansatz, Klicks auf dem 3D-`canvas` per perspektivischer Koordinaten-Mapping aufzunehmen** (Normalized-Coords → proportional auf Raster) — das ist bei einer PerspectiveCamera ungenau und unspielbar. **Stattdessen die robuste Architektur:**
```
.stage (position:relative)
└── <canvas id="level"> ← reines 3D-Rendering (Steine, Gitter, Licht)
└── <div class="grid-overlay"> ← UNICHTBARES 2D-Grid, absolut über dem Canvas
└── 9 <div class="grid-cell" data-i data-onclick> ← fängt Klicks EXAKT
```
- `grid-overlay { position:absolute; inset:0; display:grid; grid-template-columns/rows:repeat(3,1fr) }`
- Jede Zelle ist ein eigener `<div>` mit `onclick=makeMove(i)` — keine Perspektiv-Mathematik, jeder Treffer deterministisch.
- 3D-Canvas ist rein dekorativ; die Klick-Interaktion läuft 100% über das HTML-Overlay.
- Hover-Feedback (`.grid-cell:hover { background: rgba(blue,.12) }`) auf dem Overlay, nicht auf dem Canvas.
- Überprüfbar: Spielzug via `document.querySelector('.grid-cell[data-i="4"]').click()` statt fake-Koordinaten.
### 5. Verifikation (Browser-Pflicht)
Drei-D-Seiten laufen nur im Browser (WebGL). Nicht nur curl. Nutze `browser_navigate` + `browser_console`:
1. `browser_navigate(url)` — check: Seite lädt (Titel + enthält erwartete Buttons).
2. `browser_console` (ohne expression) — keine `js_errors`/`console_messages`.
3. Funktions-Test via `browser_console` mit Expression: `init()` + `makeMove(idx)` in try/catch; prüfe `board` + Anzahl platzierter Steine (`meshes.filter(m=>m!==null).length`). Bei KI-Zug (setTimeout) mit `Promise`+`setTimeout(resolve,900)` testen.
4. Nach Bug-Fix: erneut `browser_navigate` mit Cache-Buster-Query (`?fresh=N`), sonst serviert der Browser die alte Datei.
## Pitfalls
- **KEINE automatische Board/Camera-Rotation**, wenn die Seite interaktiv/spielbar sein muss — das permanente Drehen macht Klicks unberechenbar ("wegen der Drehung unspielbar"). Board still; 3D-Wirkung kommt von statischer Kamera + Stein-Platzier-Animationen.
- **2D-Symbole statt echtes 3D enttäuschen:** Nutzer will "echtes 3D" der Steine, nicht nur flache Symbole mit Schatten. Wenn "3D-Grafiken" gewünscht sind, echte Geometrie (MeshPhysicalMaterial, extrudierte Balken, Torus) rendern — nicht CSS-Schatten vortäuschen.
- `browser_console`-Expression soll klein bleiben (große Rückgaben → Stream-Timeout). Fehler isoliert in kleinen `(function(){})()`-Aufrufen zurückgeben.
- Wenn Three.js nicht mehr gebraucht wird (z.B. Wechsel auf reines 2D-Grid), `lib/` entfernen — tote Datei nicht hinterlassen.
- `chown -R www-data:www-data html/` nach dem Schreiben, sonst 403/okay nginx.
- Aufräumen: PocketBase-Tests (POST) sauber löschen (kein Test-Artefakt im echten Konto).