Performance: Animationen pausieren, Footer-Raster entschärft, WebGL nachladen

- Footer-Schriftzug (FlickeringGrid): Textmaske einmal pro Größe statt
  ~14.500 getImageData-Aufrufe pro Frame, nur flackernde Quadrate neu
  zeichnen (69–86 ms -> 0,1–0,2 ms pro Frame, Darstellung pixelgleich)
- PixelBlast: WebGL erst in Viewport-Nähe anlegen, unsichtbar pausieren
  (autoPauseOffscreen war nie angeschlossen), Shader asynchron kompilieren,
  beim Unmount aufräumen (Render-Schleifen liefen nach Seitenwechsel
  weiter); kein MSAA, keine erzwungene High-Performance-GPU
- Silk (Hero): nur rendern, solange sichtbar; DPR max. 1,5
- LightRays: keine verwaiste Render-Schleife mehr bei schnellem Vorbeiscrollen
- BorderGlow (Kontaktkarte): Sweep nur, solange die Karte sichtbar ist
- Silk, PixelBlast, LightRays als eigene Chunks (React.lazy):
  Haupt-Bundle 1,6 MB -> 646 KB (gzip 470 -> 207 KB)
- backgroud_effect und Emailsorter-Vorschaubild als WebP
  (974 KB -> 15 KB, 1,2 MB -> 11 KB), Projekt-Vorschaubilder lazy

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-25 21:47:17 +00:00
1 parent f74ddfb5ae
commit 5cc8b5f66b
13 files changed
+422 -264

No files matched your search

+22 -2
View File
@@ -127,9 +127,18 @@ const BorderGlow = ({
const angleStart = 110;
const angleEnd = 465;
let cancelled = false;
let running = false;
let visible = typeof IntersectionObserver === 'undefined';
function runSweep() {
if (cancelled || !card) return;
// Nur sweepen, solange die Karte im Bild ist – sonst malt der Browser
// Maske, Verläufe und Schatten in jedem Frame umsonst neu
if (!visible) {
running = false;
return;
}
running = true;
card.classList.add('sweep-active');
card.style.setProperty('--cursor-angle', `${angleStart}deg`);
animateValue({ duration: 500, onUpdate: v => card.style.setProperty('--edge-proximity', String(v)) });
@@ -151,8 +160,19 @@ const BorderGlow = ({
});
}
runSweep();
return () => { cancelled = true; };
if (visible) {
runSweep();
return () => { cancelled = true; };
}
const observer = new IntersectionObserver(([entry]) => {
visible = entry.isIntersecting;
if (visible && !running) runSweep();
});
observer.observe(card);
return () => {
cancelled = true;
observer.disconnect();
};
}, [animated]);
const glowVars = buildGlowVars(glowColor, glowIntensity);