A renderização de HTML em uma tela WebGL permite usar subárvores DOM ativas como texturas de GPU dinâmicas, aplicar efeitos de sombreador ao DOM ativo e integrar HTML interativo em cenas 3D.
Este guia demonstra como renderizar HTML em uma tela do WebGL. Saiba mais sobre a HTML-in-Canvas na visão geral da API.
Para renderizar HTML em uma tela do WebGL, siga estas etapas:
- Prepare sua tela. Defina os atributos certos de forma declarativa para que o navegador saiba que deve renderizar o HTML em uma tela. Essa configuração é idêntica nos contextos 2D, WebGL e WebGPU. Saiba como configurar sua tela e como declarar elementos HTML para serem desenhados de forma independente.
- Aplique o HTML como uma textura:
- Pré-alocar o armazenamento de textura da GPU.
- Faça upload do snapshot do DOM como uma textura.
- Sincronize o DOM e os pixels.
Desenhar um elemento HTML como uma textura WebGL
No WebGL, os elementos HTML são enviados para a GPU como texturas 2D usando texElementSubImage2D().
Pré-alocar armazenamento de textura
Ao contrário de uma tela 2D, que gerencia a memória de backup automaticamente, um contexto WebGL requer gerenciamento de memória explícito.
Antes de fazer upload de pixels do DOM, é necessário pré-alocar a memória da GPU. Isso permite que o navegador faça atualizações rápidas de subimagens sem acionar realocações caras de memória da GPU.
Pré-aloque o buffer com os parâmetros de tamanho selecionados e srcData definido como null da seguinte maneira:
// 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 a textura corresponda ao tamanho de layout natural do elemento HTML, use canvas.getElementImageDefaultSize() para recuperar o tamanho padrão em pixels da grade da tela:
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
);
Fazer upload do snapshot do DOM como uma textura
A próxima etapa é fazer upload do elemento HTML para a textura dentro do loop de renderização ou do listener 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()`
);
// ...
};
Sincronizar geometria
Ao contrário dos contextos 2D, o navegador não consegue inferir automaticamente onde a textura aparece na tela em um contexto 3D. É necessário alinhar manualmente o teste de hit do DOM e as caixas delimitadoras de acessibilidade com a malha 3D.
Para fazer isso, construa um DOMMatrix que mapeie a caixa de borda do elemento para o local do pixel da tela renderizada e aplique-o usando 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óximas etapas
- Entenda os principais conceitos técnicos por trás do HTML-in-Canvas.
- Saiba como renderizar HTML em um contexto 2D.
- Saiba como renderizar HTML em uma tela da WebGPU.
- Saiba mais sobre as estratégias de substituição.
- Registre um problema no GitHub para compartilhar seu feedback.
- Confira a coleção de demonstrações de HTML-in-Canvas da comunidade.