Signatur
Beschreibung
Die schreibgeschützte Eigenschaft ready der Schnittstelle ImageTrackList gehört zur WebCodecs API. Eine ImageTrackList bekommt man über ImageDecoder.tracks. Sie enthält alle Spuren eines Bildes. Die meisten Formate haben nur eine Spur. Bei animierten Formaten wie AVIF kann es aber zum Beispiel eine statische und eine animierte Spur geben.
Beim Anlegen eines ImageDecoder muss der Browser erst die Kopfdaten (Header) des Bildes lesen. Erst danach weiß er, wie viele Spuren es gibt. Bis dahin ist die Liste leer: length ist 0 und selectedTrack ist null. Mit await decoder.tracks.ready wartet man genau auf diesen Zeitpunkt. Danach kann man Spuren auswählen, frameCount und animated auslesen und decode() gezielt aufrufen.
Das Promise wird mit undefined erfüllt. Die eigentlichen Daten liest man anschließend über die Liste selbst aus, per Index (tracks[0]), selectedIndex oder selectedTrack. Das Promise wird abgelehnt, wenn das Bild nicht dekodiert werden kann (etwa bei beschädigten Daten oder einem falschen MIME-Typ) oder wenn der Decoder vorher mit close() geschlossen wird.
Rückgabewert
Promise, das mit undefined erfüllt wird, sobald die Spurliste gefüllt ist. Es wird abgelehnt (meist mit einer DOMException), wenn die Dekodierung fehlschlägt oder der Decoder geschlossen wird.Beispiele
Auf die Spuren eines GIFs warten und Metadaten auslesen
const response = await fetch('/bilder/animation.gif');
const decoder = new ImageDecoder({
data: response.body,
type: 'image/gif'
});
// Vorher ist die Liste noch leer
console.log('Vorher:', decoder.tracks.length);
await decoder.tracks.ready;
const track = decoder.tracks.selectedTrack;
console.log('Nachher:', decoder.tracks.length);
console.log('Animiert:', track.animated);
console.log('Wiederholungen:', track.repetitionCount);
Alle Frames einer Animation in ein Canvas zeichnen
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const response = await fetch('/bilder/lader.webp');
const decoder = new ImageDecoder({ data: response.body, type: 'image/webp' });
await decoder.tracks.ready;
// Bei gestreamten Daten ist frameCount erst nach completed endgültig
await decoder.completed;
const { frameCount } = decoder.tracks.selectedTrack;
for (let i = 0; i < frameCount; i++) {
const { image } = await decoder.decode({ frameIndex: i });
ctx.drawImage(image, 0, 0);
const dauerMs = (image.duration ?? 100000) / 1000; // Mikrosekunden -> ms
image.close(); // VideoFrame sofort freigeben
await new Promise((r) => setTimeout(r, dauerMs));
}
Fehlerbehandlung und Funktionserkennung
const ladeSpuren = async (url, type) => {
if (!('ImageDecoder' in window) || !(await ImageDecoder.isTypeSupported(type))) {
console.warn('ImageDecoder oder Format nicht unterstützt');
return null;
}
const data = await (await fetch(url)).arrayBuffer();
const decoder = new ImageDecoder({ data, type });
try {
await decoder.tracks.ready;
return Array.from({ length: decoder.tracks.length }, (_, i) => ({
index: i,
animated: decoder.tracks[i].animated,
frames: decoder.tracks[i].frameCount
}));
} catch (err) {
console.error('Bild konnte nicht gelesen werden:', err.name);
return null;
} finally {
decoder.close();
}
};
console.log(await ladeSpuren('/bilder/foto.avif', 'image/avif'));
// Wichtig · Fallstricke
- Browser-Unterstützung:
ImageDecodergibt es in Chromium-Browsern ab Version 94 und in Firefox ab Version 133. Safari unterstützt es nicht. Die API funktioniert nur in sicheren Kontexten (HTTPS) und steht auch in Web Workern zur Verfügung. Deshalb vorher auf'ImageDecoder' in selfprüfen. readygarantiert nur, dass die Spuren bekannt sind. Werden die Daten alsReadableStreamgeliefert, kannframeCountzu diesem Zeitpunkt noch zu klein sein. Den endgültigen Wert gibt es erst nachawait decoder.completed.- Das Promise wird mit
undefinederfüllt, nicht mit der Liste. Also nichtconst tracks = await decoder.tracks.readyschreiben. - Wird
decoder.close()aufgerufen, bevor das Promise erfüllt ist, wird es abgelehnt. Ohnetry/catchführt das zu einer unbehandelten Ablehnung. - Von
decode()gelieferteVideoFrame-Objekte belegen GPU- bzw. Arbeitsspeicher. Sie sollten immer mitclose()freigegeben werden.