Start · Sprachen · JavaScript · Referenz · HTMLCanvasElement.transferControlToOffscreen()

HTMLCanvasElement.transferControlToOffscreen()

Methode

Überträgt die Zeichenkontrolle eines <code>&lt;canvas&gt;</code>-Elements an ein <code>OffscreenCanvas</code>, das im Haupt-Thread oder in einem Worker rendern kann.

seit JavaScript Baseline 2023: Chrome 69, Firefox 105, Safari 16.4 Kategorie: webapi

Signatur

HTMLCanvasElement.prototype.transferControlToOffscreen()

Beschreibung

transferControlToOffscreen() erzeugt ein OffscreenCanvas, das fest mit dem sichtbaren <canvas>-Element verbunden ist. Alles, was auf dem OffscreenCanvas gezeichnet wird, erscheint automatisch im Element auf der Seite. So kann man das Rendern komplett aus dem Haupt-Thread herausnehmen.

Am häufigsten überträgt man das zurückgegebene Objekt per postMessage() als übertragbares Objekt (Transferable) an einen Worker. Dort holt man mit getContext("2d"), "webgl", "webgl2" oder "webgpu" einen Kontext und zeichnet, z. B. in einer requestAnimationFrame-Schleife. Aufwendige Animationen, Diagramme oder Spiele laufen dann flüssig weiter, auch wenn der Haupt-Thread mit DOM-Arbeit, Layout oder Skripten beschäftigt ist.

Die Methode kann pro Element nur einmal aufgerufen werden, und das Element darf vorher noch keinen Rendering-Kontext haben. Danach lässt sich auf dem Element selbst kein getContext() mehr aufrufen. Die Größe der Zeichenfläche steuert man anschließend über width/height des OffscreenCanvas, nicht mehr über das Element.

Rückgabewert

Typ
OffscreenCanvas
Beschreibung
Ein OffscreenCanvas, dessen Inhalt im ursprünglichen <canvas>-Element angezeigt wird. Seine Anfangsgröße entspricht den Attributen width und height des Elements.

Beispiele

Rendern in einen Worker auslagern (Haupt-Thread)

// main.js
const canvas = document.querySelector('#chart');
const offscreen = canvas.transferControlToOffscreen();

const worker = new Worker(new URL('./render-worker.js', import.meta.url), { type: 'module' });

// Zweites Argument: Transfer-Liste – das Objekt wird verschoben, nicht kopiert
worker.postMessage({ type: 'init', canvas: offscreen }, [offscreen]);

Animationsschleife im Worker

// render-worker.js
let ctx;
let x = 0;

const draw = () => {
  const { width, height } = ctx.canvas;
  ctx.clearRect(0, 0, width, height);
  ctx.fillStyle = '#0a7';
  ctx.fillRect(x, height / 2 - 20, 40, 40);
  x = (x + 2) % width;
  requestAnimationFrame(draw); // in Dedicated Workers verfügbar
};

self.onmessage = ({ data }) => {
  if (data.type === 'init') {
    ctx = data.canvas.getContext('2d');
    draw();
  }
};

Größe bei Fensteränderung anpassen

// main.js – Größenänderung an den Worker melden
const resize = () => {
  const dpr = window.devicePixelRatio || 1;
  worker.postMessage({
    type: 'resize',
    width: Math.round(canvas.clientWidth * dpr),
    height: Math.round(canvas.clientHeight * dpr)
  });
};
window.addEventListener('resize', resize);
resize();

// render-worker.js – im onmessage-Handler ergänzen:
// if (data.type === 'resize') {
//   ctx.canvas.width = data.width;
//   ctx.canvas.height = data.height;
// }

Fehler beim doppelten Aufruf abfangen

const canvas = document.createElement('canvas');
const first = canvas.transferControlToOffscreen();

try {
  canvas.transferControlToOffscreen();
} catch (err) {
  console.log(err.name);
}

try {
  canvas.getContext('2d');
} catch (err) {
  console.log(err.name);
}
InvalidStateError InvalidStateError

// Wichtig · Fallstricke

  • Nur einmal aufrufbar: Ein zweiter Aufruf wirft eine InvalidStateError-DOMException. Das passiert auch, wenn das Element schon einen Kontext über getContext() hat.
  • Transfer-Liste nicht vergessen: Ohne [offscreen] als zweites Argument von postMessage() gibt es einen DataCloneError, weil sich ein OffscreenCanvas nicht klonen lässt.
  • Größe: Wenn man canvas.width/height am Element nach dem Transfer ändert, wirft das eine InvalidStateError. Die Größe setzt man stattdessen am OffscreenCanvas, also im Worker. Die CSS-Größe des Elements lässt sich weiterhin ändern.
  • Methoden wie toDataURL() und toBlob() am Element funktionieren nach dem Transfer nicht mehr. Nutze im Worker OffscreenCanvas.convertToBlob().
  • Im Worker gibt es keinen DOM-Zugriff. Eingaben wie Maus- oder Tastaturereignisse muss man im Haupt-Thread abfangen und per postMessage() weiterreichen.
  • Kompatibilität: Chrome/Edge ab 69, Firefox ab 105, Safari ab 16.4 (WebGL im Worker erst ab Safari 17). Für ältere Browser prüft man 'transferControlToOffscreen' in HTMLCanvasElement.prototype und rendert sonst im Haupt-Thread.