HTML im WebGL-Canvas

Wenn Sie HTML in einem WebGL-Canvas rendern, können Sie Live-DOM-Unterbäume als dynamische GPU-Texturen verwenden, Shader-Effekte auf das Live-DOM anwenden und interaktives HTML in 3D-Szenen einbinden.

In diesem Leitfaden wird gezeigt, wie HTML in einem WebGL-Canvas gerendert wird. Weitere Informationen zu HTML-in-Canvas finden Sie in der API-Übersicht.

Zum Rendern von HTML in einem WebGL-Canvas sind die folgenden Schritte erforderlich:

  1. Arbeitsfläche vorbereiten Legen Sie die richtigen Attribute deklarativ fest, damit der Browser weiß, dass er HTML in einem Canvas rendern soll. Diese Einrichtung ist für 2D-, WebGL- und WebGPU-Kontexte identisch. Informationen zum Einrichten des Canvas und zum Deklarieren von HTML-Elementen, die unabhängig voneinander gezeichnet werden sollen
  2. Wenden Sie den HTML-Code als Textur an:
    1. GPU-Texturspeicher vorab zuweisen.
    2. Laden Sie den DOM-Snapshot als Textur hoch.
  3. Synchronisieren Sie das DOM und die Pixel.

HTML-Element als WebGL-Textur zeichnen

In WebGL werden HTML-Elemente mit texElementSubImage2D() als 2D-Texturen auf die GPU hochgeladen.

Texturspeicher vorab zuweisen

Im Gegensatz zu einem 2D-Canvas, bei dem der zugrunde liegende Speicher automatisch verwaltet wird, ist für einen WebGL-Kontext eine explizite Speicherverwaltung erforderlich.

Bevor Sie DOM-Pixel hochladen, müssen Sie GPU-Speicher vorab zuweisen. So kann der Browser schnelle Updates von Teilbildern durchführen, ohne dass teure Neuzuweisungen von GPU-Arbeitsspeicher ausgelöst werden.

Weisen Sie dem Zwischenspeicher vorab die ausgewählten Größenparameter zu und setzen Sie srcData auf null:

// Only allocate texture backing once
if (!textureBufferAllocated) {
  // Pre-allocate texture backing with pre-determined size.
  gl.texImage2D(
    /* target */ gl.TEXTURE_2D,
    /* level */ 0,
    /* internalformat */ gl.RGBA8,
    /* width */ myElementWidth,
    /* height */ myElementHeight,
    /* border */ 0,
    /* format */ gl.RGBA,
    /* type */ gl.UNSIGNED_BYTE,
    /* srcData */ null
  );

  textureBufferAllocated = true;
}

Damit die Textur der natürlichen Layoutgröße des HTML-Elements entspricht, verwenden Sie canvas.getElementImageDefaultSize(), um die Standardgröße in Canvas-Rasterpixeln abzurufen:

const { width, height } = canvas.getElementImageDefaultSize(myElement);
// Pre-allocate texture backing. Only do this once, or whenever the element
// changes size.
gl.texImage2D(
  /* target */ gl.TEXTURE_2D,
  /* level */ 0,
  /* internalformat */ gl.RGBA8,
  /* width */ Math.ceil(width),
  /* height */ Math.ceil(height),
  /* border */ 0,
  /* format */ gl.RGBA,
  /* type */ gl.UNSIGNED_BYTE,
  /* srcData */ null
);

DOM-Snapshot als Textur hochladen

Im nächsten Schritt laden Sie das HTML-Element in Ihre Textur in Ihrer Render-Schleife oder Ihrem paint-Listener hoch.

canvas.onpaint = () => {
  // 1. Pre-allocate texture backing first
  // ...

  // 2. Upload HTML snapshot into the WebGL texture
  gl.texElementSubImage2D(
    /* target  */ gl.TEXTURE_2D,
    /* level   */ 0,
    /* xoffset */ 0,
    /* yoffset */ 0,
    /* element */ myElement // or an ElementImage from `captureElementImage()`
  );

  // ...
};

Geometrie synchronisieren

Im Gegensatz zu 2D-Kontexten kann der Browser in einem 3D-Kontext nicht automatisch ableiten, wo die Textur auf dem Bildschirm angezeigt wird. Sie müssen die DOM-Trefferprüfung und die Begrenzungsrahmen für die Barrierefreiheit im Internet manuell an Ihr 3D-Mesh anpassen.

Erstellen Sie dazu eine DOMMatrix, die die Rahmenbox des Elements der gerenderten Canvas-Pixelposition zuordnet, und wenden Sie sie mit updateElementGeometry() an:

canvas.addEventListener("paint", () => {
  // 1. Upload fresh DOM snapshot to GPU texture
  gl.bindTexture(gl.TEXTURE_2D, myTexture);
  gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, myElement);

  // 2. Draw your 3D scene
  draw3DScene();

  // 3. Construct the CSS-pixel transform mapping the element's
  // border box to its drawn position on the canvas as needed
  // (for example: scale and translate)
  const canvasTransform = new DOMMatrix();
  // scale X
  canvasTransform.a = canvas.clientWidth / myElement.offsetWidth;
  // scale Y
  canvasTransform.d = canvas.clientHeight / myElement.offsetHeight;
  // translate X (CSS px)
  canvasTransform.e = 50;
  // translate Y (CSS px)
  canvasTransform.f = 100;

  // 4. Update DOM position, accessibility focus rings, and pointer hit-testing
  canvas.updateElementGeometry(myElement, {
    canvasTransform: canvasTransform,
  });
});

Nächste Schritte