74 lines
5.3 KiB
Markdown
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).
|