# Verbesserungsvorschläge: floating-lines.js Analyse von `floating-lines.js` (Dev-Klasse) im Vergleich zur produktiven `FloatingLines.vue`. **Stand: April 2026 — alle wesentlichen Punkte umgesetzt.** --- ## Was der Code macht (Kurzübersicht) Ein WebGL-Fullscreen-Shader via Three.js mit drei visuellen Schichten: - **Top/Bottom**: Einfache Sinus-Wellen mit Rotation (`wave()`) - **Middle**: Bézier-Kurven zwischen Kontrollpunkten mit animierten Fächer-Linien (`waveFocal()`) + Kreise an den Punkten - **Hintergrund**: Radialer Verlauf von Mitte → Rand (oder Horizont-Split bei Modus „Trennung") --- ## Fehler / Bugs ### 1. Totes Uniform `bgColor` ✅ Umgesetzt **Datei:** `floating-lines.js`, Zeile 508 Das tote `bgColor`-Uniform wurde entfernt. Der Shader nutzt ausschließlich `bgColorCenter` + `bgColorEdge`. ```js // Entfernt: bgColor: { value: new Vector3(0, 0, 0) }, ``` ### 2. Konstruktor-Parameter `middleWavePosition` wird ignoriert ✅ Umgesetzt Der ungenutzte Parameter wurde aus der Konstruktorsignatur entfernt. ### 3. Kein Konstruktor-Interface für `bgColorCenter` / `bgColorEdge` ✅ Umgesetzt Beide Uniforms sind jetzt als Konstruktor-Parameter verfügbar und werden korrekt initialisiert: ```js constructor({ ..., bgColorCenter = '#0a0514', bgColorEdge = '#000000', ... }) ``` Die Uniforms werden beim Konstruktoraufruf aus den Hex-Strings in `Vector3`-Werte konvertiert. --- ## Performance-Problem ### 4. `bezierClosestT` wird pro Linie neu berechnet (kritisch) ✅ Umgesetzt `bezierClosestT` wird jetzt **einmal pro Segment** berechnet. Die Ergebnisse (`bt`, `bPos`, `bNorm`) werden als Parameter an `waveFocal()` übergeben: ```glsl // Neue waveFocal()-Signatur (precomputed values): float waveFocal(vec2 uv, float fi, float totalLines, float t, vec2 bPos, vec2 bNorm) // Im Segment-Loop (einmal pro Segment, nicht pro Linie): float bt = bezierClosestT(baseUv, sp, pc, ep); vec2 bPos = bmt*bmt*sp + 2.0*bmt*bt*pc + bt*bt*ep; vec2 bTang = normalize(2.0*bmt*(pc - sp) + 2.0*bt*(ep - pc)); vec2 bNorm = vec2(-bTang.y, bTang.x); // → alle middleLineCount Aufrufe nutzen dieselben Werte ``` Reduktion von O(Segmente × Linien) auf O(Segmente) `bezierClosestT`-Aufrufe pro Pixel. --- ## Fehlende Features (in `.vue` vorhanden, in `.js` nicht) ### 5. `lineBrightness` Uniform fehlt ✅ Umgesetzt `lineBrightness` ist jetzt als Konstruktor-Parameter und Uniform vorhanden. Im Shader: ```glsl col *= lineBrightness; // vor Background-Composite ``` ### 6. Kein Pause bei verstecktem Tab ✅ Umgesetzt Der `requestAnimationFrame`-Loop pausiert jetzt bei `document.hidden`: ```js this._handleVisibility = () => { if (document.hidden) { cancelAnimationFrame(this.raf) this.raf = 0 } else if (!this.raf) { renderLoop() } } document.addEventListener('visibilitychange', this._handleVisibility) // destroy() ruft removeEventListener auf ``` ### 7. Kein adaptives DPR ⏭️ Offen / Optional Die Vue-Version misst FPS live und reduziert `devicePixelRatio` bei schlechter Performance (vor allem Mobile). Die Dev-Klasse ist ein Test-Tool und nicht für Mobile ausgelegt — diese Komplexität lohnt sich hier nicht. --- ## Code-Qualität ### 8. Legacy-Code: `background_color()`, `BLACK`, `PINK`, `BLUE` ✅ Umgesetzt Die Shader-Konstanten `BLACK`, `PINK`, `BLUE` und die Funktion `background_color()` wurden entfernt. Der Hintergrund wird immer über `bgColorCenter`/`bgColorEdge` gesteuert. ### 9. Redundante `enabledWaves.includes()` Checks ✅ Umgesetzt Die doppelten Prüfungen in den Hilfsfunktionen wurden entfernt. Die äußere Prüfung im Aufrufer ist die einzige Guard. ### 10. Hardcoded `* 0.5` in `getLineColor()` ✅ Umgesetzt Der feste `* 0.5`-Faktor wurde entfernt. `getLineColor()` gibt jetzt die volle Gradient-Farbe zurück. Die Kompensation mit `* 2.5` an den Kreisen wurde auf `* 1.5` angepasst. Die Helligkeit wird über das `lineBrightness`-Uniform gesteuert (→ Punkt 5). --- ## Optionale Verbesserungen / Ideen ### 11. Glättere Kreise bei höherem DPR ⏭️ Offen / Optional Der AA-Radius passt sich durch `iResolution` (physische Pixel bei gesetztem DPR) bereits implizit an. Keine Änderung nötig. ### 12. `pointSpacingX` + `pointsOffsetX` vs. explizite X-Koordinaten ⏭️ Offen / Optional Die Dev-Klasse behält das Auto-Spacing-Modell für einfache Testzwecke. Die Vue-Komponente nutzt explizite X-Koordinaten für die Lebenszeitlinie. Beide Ansätze sind intentional verschieden. ### 13. GLSL `precision highp` → `mediump` ✅ Umgesetzt Fragment-Shader nutzt jetzt `precision mediump float` — ausreichend für diese Visualisierung, effizienter auf Mobile/Low-End. --- ## Neue Punkte (nachträglich ergänzt) ### 14. Resize-Bug: Kreise und Linien desynchronisieren sich ✅ Umgesetzt **Betrifft:** `LifeWaveLayout.vue` **Ursache:** `layoutResizeObserver` aktualisierte `layoutWidth/Height` sofort, während `TimelineView`s `@view-update` (mit den neuen CSS-Event-Positionen) erst im nächsten Frame kam — führte zu 1-Frame UV-Desync. **Fix:** `requestAnimationFrame`-Wrapper im Callback: ```js layoutResizeObserver = new ResizeObserver(() => { requestAnimationFrame(() => { if (!layoutRef.value) return layoutWidth.value = layoutRef.value.clientWidth layoutHeight.value = layoutRef.value.clientHeight }) }) ``` ### 15. Feature: Horizont ✅ Umgesetzt (erweitert) Statt einer einzelnen Linie wurden **drei wählbare Horizont-Modi** implementiert: | Modus | Wert | Beschreibung | |-------|------|--------------| | Aus | `'off'` | Kein Horizont-Effekt | | Nebel | `'fog'` | Leuchtender Band-Effekt auf Y=0, Farbe aus Gradient | | Trennung | `'split'` | Hintergrund wird vertikal geteilt: `bgColorCenter` oben, `bgColorEdge` unten — mit einstellbarer Blend-Breite | | Glow | `'glow'` | Weiches + hartes Leuchten auf dem Horizont, Farbe aus Gradient | **Shader (alle Modi):** ```glsl uniform int horizonMode; // 0=off 1=fog 2=split 3=glow uniform float horizonOpacity; uniform float horizonBlend; if (horizonMode == 1) { float band = exp(-baseUv.y * baseUv.y * 5.0); vec3 fogColor = getLineColor(0.5, bg) * 2.0; col += fogColor * band * horizonOpacity; } else if (horizonMode == 2) { float blendW = max(horizonBlend * 0.7, 0.001); float t = smoothstep(-blendW, blendW, baseUv.y); bg = mix(bgColorEdge, bgColorCenter, t); } else if (horizonMode == 3) { float d2 = baseUv.y * baseUv.y; float softGlow = exp(-d2 * 10.0); float coreGlow = exp(-d2 * 70.0) * 0.7; vec3 glowColor = getLineColor(0.5, bg) * 3.0; col += glowColor * (softGlow + coreGlow) * horizonOpacity; } ``` **Umgesetzt in:** - `floating-lines.js` — Shader + Konstruktor-Parameter + Uniforms - `FloatingLines.vue` — Shader (mit `gradientMid()` Hilfsfunktion) + Props + Uniforms + Watches - `settings.js` — `horizonMode: 'off'`, `horizonOpacity: 0.5`, `horizonBlend: 0.2` - `LifeWaveSettings.vue` — Segmented Control + bedingte Slider (Deckkraft / Übergang) - `LifeWaveLayout.vue` — Props weitergeleitet - `init-fl.html` — 4 Modus-Buttons + bedingte Slider-Sichtbarkeit --- ## Zusammenfassung Prioritäten | # | Typ | Priorität | Aufwand | Status | |---|-----|-----------|---------|--------| | 4 | Performance: `bezierClosestT` Hoisting | **Hoch** | Mittel | ✅ | | 14 | Bug: Resize-Desync (LifeWaveLayout) | **Hoch** | Minimal | ✅ | | 1 | Bug: totes `bgColor` Uniform | Mittel | Minimal | ✅ | | 3 | Bug: `bgColorCenter/Edge` nicht setzbar | Mittel | Klein | ✅ | | 5 | Feature: `lineBrightness` | Mittel | Klein | ✅ | | 10 | Qualität: hardcoded `* 0.5` | Mittel | Klein | ✅ | | 15 | Feature: Horizont (3 Modi) | Mittel | Mittel | ✅ | | 8 | Qualität: Legacy-Code entfernen | Niedrig | Klein | ✅ | | 6 | Feature: Tab-Pause | Niedrig | Klein | ✅ | | 2 | Bug: ignorierter Parameter | Niedrig | Minimal | ✅ | | 9 | Qualität: redundante Checks | Niedrig | Minimal | ✅ | | 13 | Perf: `mediump` Precision | Optional | Minimal | ✅ | | 7 | Feature: adaptives DPR | Optional | Groß | ⏭️ | | 11 | Qualität: AA-Radius explizit | Optional | Minimal | ⏭️ | | 12 | API: explizite X-Koordinaten | Optional | Mittel | ⏭️ |