8 KiB
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 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 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 + UniformsFloatingLines.vue— Shader (mitgradientMid()Hilfsfunktion) + Props + Uniforms + Watchessettings.js—horizonMode: 'off',horizonOpacity: 0.5,horizonBlend: 0.2LifeWaveSettings.vue— Segmented Control + bedingte Slider (Deckkraft / Übergang)LifeWaveLayout.vue— Props weitergeleitetinit-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 | ⏭️ |