thats-me/frontend/dev/IMPROVEMENTS-floating-lines.md
2026-04-30 14:54:39 +02:00

8 KiB
Raw Permalink Blame History

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.

// 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:

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:

// 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:

col *= lineBrightness;  // vor Background-Composite

6. Kein Pause bei verstecktem Tab Umgesetzt

Der requestAnimationFrame-Loop pausiert jetzt bei document.hidden:

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 highpmediump 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 TimelineViews @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:

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):

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.jshorizonMode: '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 ⏭️