Renderizar HTML en un lienzo de WebGL te permite usar subárboles del DOM en vivo como texturas dinámicas de GPU, aplicar efectos de sombreador al DOM en vivo y, además, integrar HTML interactivo en escenas en 3D.
En esta guía, se muestra cómo renderizar HTML en un lienzo de WebGL. Obtén más información sobre HTML-in-Canvas en la descripción general de la API.
Para renderizar HTML en un lienzo de WebGL, se deben seguir estos pasos:
- Prepara tu lienzo. Establecer de forma declarativa los atributos correctos para que el navegador sepa que debe renderizar HTML en un lienzo Esta configuración es idéntica en los contextos de 2D, WebGL y WebGPU. Obtén más información para configurar tu lienzo y declarar elementos HTML que se dibujarán de forma independiente.
- Aplica el HTML como una textura:
- Asigna previamente el almacenamiento de texturas de la GPU.
- Sube la instantánea del DOM como una textura.
- Sincroniza el DOM y los píxeles.
Dibuja un elemento HTML como una textura de WebGL
En WebGL, los elementos HTML se suben a la GPU como texturas 2D con texElementSubImage2D().
Asigna previamente el almacenamiento de texturas
A diferencia de un lienzo en 2D, que administra la memoria de respaldo automáticamente, un contexto de WebGL requiere una administración de la memoria explícita.
Antes de subir píxeles del DOM, debes asignar previamente memoria de la GPU. Esto permite que el navegador realice actualizaciones rápidas de subimágenes sin activar reasignaciones costosas de memoria de GPU.
Asigna previamente el búfer con los parámetros de tamaño seleccionados y srcData establecido en null de la siguiente manera:
// 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;
}
Para que la textura coincida con el tamaño de diseño natural del elemento HTML, usa canvas.getElementImageDefaultSize() para recuperar su tamaño predeterminado en píxeles de la cuadrícula del lienzo:
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
);
Sube la instantánea del DOM como una textura.
El siguiente paso es subir el elemento HTML a tu textura dentro del bucle de renderización o el objeto de escucha 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()`
);
// ...
};
Sincroniza la geometría
A diferencia de los contextos en 2D, el navegador no puede inferir automáticamente dónde aparece la textura en la pantalla en un contexto en 3D. Debes alinear manualmente las pruebas de posicionamiento del DOM y los cuadros delimitadores de accesibilidad con tu malla 3D.
Para ello, construye un DOMMatrix que asigne el cuadro de borde del elemento a su ubicación de píxeles del lienzo renderizado y, luego, aplícalo con 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,
});
});
Próximos pasos
- Comprende los conceptos técnicos básicos de HTML-in-Canvas.
- Obtén información para renderizar HTML en un contexto 2D.
- Obtén más información para renderizar HTML en un lienzo de WebGPU.
- Obtén más información sobre las estrategias de resguardo.
- Informa un problema en GitHub para compartir tus comentarios.
- Consulta la colección de demostraciones de HTML-in-Canvas de la comunidad.