Start · Sprachen · JavaScript · Referenz · PerformanceLongAnimationFrameTiming

PerformanceLongAnimationFrameTiming

Schnittstelle

Liefert detaillierte Zeitmesswerte zu langen Animationsframes (LoAFs, über 50 ms), die das Rendering verzögern und den Hauptthread blockieren.

seit JavaScript Chrome/Edge 123+ (experimentell, nicht in Firefox/ Kategorie: webapi

Signatur

interface PerformanceLongAnimationFrameTiming extends PerformanceEntry

Beschreibung

PerformanceLongAnimationFrameTiming ist Teil der Long Animation Frames API (LoAF) und ersetzt in der Praxis die ältere Long Tasks API. Statt einzelner langer Tasks misst sie ganze Animationsframes: den Zeitraum vom Start der ersten Task bis zum Zeichnen des Frames, inklusive Rendering-Arbeit wie requestAnimationFrame-Callbacks, Stilberechnung und Layout. Ein Eintrag wird erzeugt, wenn ein Frame länger als 50 ms dauert.

Instanzen erzeugt man nicht selbst. Man erhält sie über einen PerformanceObserver mit dem Eintragstyp "long-animation-frame". Wichtige Eigenschaften neben den geerbten startTime, duration, name und entryType:

  • blockingDuration: Zeit in Millisekunden, in der der Hauptthread blockiert war (Summe der Anteile langer Tasks über 50 ms plus Renderzeit).
  • renderStart: Beginn des Rendering-Zyklus (inklusive requestAnimationFrame-Callbacks und ResizeObserver).
  • styleAndLayoutStart: Beginn der Stil- und Layoutberechnung.
  • firstUIEventTimestamp: Zeitpunkt des ersten UI-Ereignisses (z. B. Klick oder Tastendruck), das in diesem Frame verarbeitet wurde.
  • scripts: Array von PerformanceScriptTiming-Objekten mit Zuordnung zu Skripten (Quell-URL, Funktionsname, Aufrufer, erzwungenes Layout u. a.).
  • toJSON(): gibt eine JSON-Darstellung des Eintrags zurück.

Typische Anwendungsfälle sind Real User Monitoring (RUM) und die Diagnose schlechter Interaction to Next Paint (INP)-Werte: Über scripts lässt sich genau erkennen, welches Skript bzw. welche Funktion einen Frame ausgebremst hat, etwa ein Drittanbieter-Tag oder ein aufwendiger Event-Handler.

Rückgabewert

Typ
PerformanceLongAnimationFrameTiming
Beschreibung
Wird nicht per Konstruktor erzeugt, sondern von PerformanceObserver bzw. performance.getEntriesByType() geliefert. entryType und name haben beide den Wert "long-animation-frame".

Beispiele

Lange Animationsframes beobachten

// Feature-Erkennung vor der Nutzung
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log(
        `LoAF: ${entry.duration.toFixed(0)} ms, ` +
        `blockiert: ${entry.blockingDuration.toFixed(0)} ms`
      );
    }
  });
  // buffered: true liefert auch Einträge vor der Registrierung
  observer.observe({ type: 'long-animation-frame', buffered: true });
}
LoAF: 142 ms, blockiert: 97 ms

Verursachende Skripte ermitteln

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    for (const script of entry.scripts) {
      console.log({
        quelle: script.sourceURL,
        funktion: script.sourceFunctionName,
        aufrufer: script.invoker,
        dauer: Math.round(script.duration),
        erzwungenesLayout: Math.round(script.forcedStyleAndLayoutDuration)
      });
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });
{ quelle: 'https://cdn.example.com/widget.js', funktion: 'renderList', aufrufer: 'BUTTON#save.onclick', dauer: 88, erzwungenesLayout: 21 }

Phasen eines Frames aufschlüsseln

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    const ende = entry.startTime + entry.duration;
    const skriptZeit = entry.renderStart - entry.startTime;
    const renderZeit = ende - entry.renderStart;
    const layoutZeit = ende - entry.styleAndLayoutStart;
    console.log(`Skripte: ${skriptZeit.toFixed(0)} ms, ` +
      `Rendering: ${renderZeit.toFixed(0)} ms, ` +
      `davon Stil/Layout: ${layoutZeit.toFixed(0)} ms`);
  }
});
observer.observe({ type: 'long-animation-frame' });
Skripte: 110 ms, Rendering: 32 ms, davon Stil/Layout: 18 ms

LoAF-Daten an Monitoring senden

const observer = new PerformanceObserver((list) => {
  const payload = list.getEntries()
    .filter((e) => e.blockingDuration > 100)
    .map((e) => e.toJSON());
  if (payload.length > 0) {
    navigator.sendBeacon('/rum/loaf', JSON.stringify(payload));
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

// Wichtig · Fallstricke

  • Browser-Kompatibilität: Nur in Chromium-Browsern (Chrome/Edge ab Version 123) verfügbar; Firefox und Safari unterstützen die API nicht. Immer per PerformanceObserver.supportedEntryTypes.includes('long-animation-frame') prüfen.
  • Nicht über performance.getEntries() ohne Weiteres zuverlässig abrufbar – verwenden Sie einen PerformanceObserver. Der interne Puffer für buffered: true ist begrenzt (ca. 200 Einträge).
  • Die Zuordnung in scripts gilt nur für Skripte, deren Einzeldauer über 5 ms liegt. Bei Skripten aus anderen Ursprüngen ohne passende CORS-Freigabe (crossorigin-Attribut) fehlen Angaben wie sourceFunctionName teilweise.
  • Nur Arbeit im Hauptthread des eigenen Fensters (und gleichursprünglicher Frames) wird erfasst, Web Worker nicht.
  • duration und blockingDuration unterscheiden sich: Ein Frame kann lang sein, ohne stark zu blockieren. Für die Bewertung der Reaktionsfähigkeit ist blockingDuration meist aussagekräftiger.
  • Beim Senden an Monitoring-Dienste auf Datenmenge achten und URLs gegebenenfalls kürzen oder anonymisieren (Datenschutz).