HTML di kanvas WebGL

Merender HTML di kanvas WebGL memungkinkan Anda menggunakan subtree DOM aktif sebagai tekstur GPU dinamis, menerapkan efek shader ke DOM aktif, dan mengintegrasikan HTML interaktif ke dalam scene 3D.

Panduan ini menunjukkan cara merender HTML ke dalam kanvas WebGL. Pelajari lebih lanjut HTML-in-Canvas di ringkasan API.

Merender HTML di kanvas WebGL memerlukan langkah-langkah berikut:

  1. Siapkan kanvas Anda. Tetapkan atribut yang tepat secara deklaratif untuk memberi tahu browser bahwa browser harus merender HTML ke dalam kanvas. Penyiapan ini identik di seluruh konteks 2D, WebGL, dan WebGPU. Pelajari cara menyiapkan kanvas dan cara mendeklarasikan elemen HTML yang akan digambar secara independen.
  2. Terapkan HTML sebagai tekstur:
    1. Pra-alokasikan penyimpanan tekstur GPU.
    2. Upload snapshot DOM sebagai tekstur.
  3. Menyinkronkan DOM dan piksel.

Menggambar elemen HTML sebagai tekstur WebGL

Di WebGL, elemen HTML diupload ke GPU sebagai tekstur 2D menggunakan texElementSubImage2D().

Melakukan pra-alokasi penyimpanan tekstur

Tidak seperti kanvas 2D, yang mengelola memori pendukung secara otomatis, konteks WebGL memerlukan pengelolaan memori eksplisit.

Sebelum mengupload piksel DOM, Anda harus mengalokasikan memori GPU terlebih dahulu. Hal ini memungkinkan browser melakukan update sub-gambar dengan cepat tanpa memicu realokasi memori GPU yang mahal.

Alokasikan buffer terlebih dahulu dengan parameter ukuran yang dipilih dan srcData ditetapkan ke null sebagai berikut:

// 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;
}

Agar tekstur sesuai dengan ukuran tata letak alami elemen HTML, gunakan canvas.getElementImageDefaultSize() untuk mengambil ukuran defaultnya dalam piksel petak kanvas:

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
);

Mengupload snapshot DOM sebagai tekstur

Langkah berikutnya adalah mengupload elemen HTML ke tekstur di dalam loop render atau pemroses paint.

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()`
  );

  // ...
};

Menyinkronkan geometri

Tidak seperti dalam konteks 2D, browser tidak dapat otomatis menyimpulkan tempat tekstur muncul di layar dalam konteks 3D. Anda harus menyelaraskan hit-test DOM dan kotak pembatas aksesibilitas dengan mesh 3D secara manual.

Untuk melakukannya, buat DOMMatrix yang memetakan kotak batas elemen ke lokasi piksel kanvas yang dirender, lalu terapkan menggunakan updateElementGeometry():

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,
  });
});

Langkah berikutnya