O Chrome 150 e o 155 introduziram várias mudanças na API HTML no Canvas. Essas mudanças responderam ao feedback da comunidade e dos padrões com o objetivo de melhorar a ergonomia da API. Esta postagem do blog descreve as mudanças e como garantir que sua implementação esteja atualizada com elas.
As atualizações incluem a renomeação de atributos e métodos, exigindo pré-alocação explícita de memória de textura e atualização de modelos de sincronização do DOM. Além disso, o teste de origem do HTML no Canvas foi estendido. A seguir, há uma explicação sobre o que está mudando, por quê e como atualizar sua base de código.
Mudanças no Chrome 150
No Chrome 150, mudamos as assinaturas de argumentos no WebGL e no WebGPU para eliminar a redundância e alinhar com as convenções gráficas modernas.
WebGL
Removemos argumentos redundantes (level, srcFormat, destType) de texElementImage2D. Consulte a discussão sobre o WebGL para mais detalhes:
// Old implementation (Chrome < 150)
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const destType = gl.UNSIGNED_BYTE;
gl.texElementImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
srcFormat,
destType,
element
);
// New implementation (from Chrome 150)
const internalFormat = gl.RGBA8;
gl.texElementImage2D(gl.TEXTURE_2D, internalFormat, element);
WebGPU
Padronizamos os parâmetros copyElementImageToTexture em objetos sourceDict e destDict. Consulte GPUWeb PR#6250:
// Old implementation (Chrome < 150)
GPUQueue.copyElementImageToTexture(
element,
width,
height,
{ texture: texture }
);
// New implementation (from Chrome 150)
const sourceDict = { source: element };
const destDict = {
destination: { texture: texture },
width: width,
height: height
};
GPUQueue.copyElementImageToTexture(sourceDict, destDict);
Mudanças no Chrome 155
O Chrome 155 apresenta mais refinamentos com base no feedback dos desenvolvedores. Estamos atualizando nomes de métodos, exigindo pré-alocação explícita de memória de textura, compatibilidade com subárvores DOM complexas e desacoplando a sincronização do DOM.
WebGPU
O método WebGPU foi renomeado de copyElementImageToTexture para
drawElementImageToTexture. Além da mudança de nome, as coordenadas de parâmetro são expandidas (sourceX, sourceWidth), e os limites de destino são agrupados em um objeto size (GPUExtent3D) padrão:
// BEFORE (Chrome 150)
const sourceDict = {
source: myElement,
sx: 0,
sy: 0,
swidth: 100,
sheight: 100
};
const destDict = {
destination: { texture: myTexture },
width: 300,
height: 200
};
queue.copyElementImageToTexture(sourceDict, destDict);
// AFTER (Chrome 155+)
const sourceDict = {
source: myElement,
sourceX: 0,
sourceY: 0,
sourceWidth: 100,
sourceHeight: 100
};
const destDict = {
texture: myTexture,
size: { width: 300, height: 200 }
};
queue.drawElementImageToTexture(sourceDict, destDict);
WebGL
Há três aspectos nas mudanças do WebGL para HTML-in-Canvas:
- O nome do método muda de
texElementImage2DparatexElementSubImage2D.` - A assinatura do método muda. Principalmente, ela adiciona argumentos
xoffseteyoffsetpara permitir especificar um deslocamento na textura de destino em que o elemento será desenhado. - O método não aloca mais automaticamente o armazenamento de buffer para a textura de destino. O armazenamento precisa ser alocado explicitamente antes de usar a API. Isso é semelhante ao comportamento de
texSubImage2D.
// 1. Capture natural layout size of the element
const elementImage = canvas.captureElementImage(element);
// 2. Pre-allocate texture backing once (or when element resizes)
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA8,
Math.ceil(elementImage.width),
Math.ceil(elementImage.height),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null // null reserves VRAM without uploading pixels
);
// 3. Upload DOM into pre-allocated VRAM
gl.texElementSubImage2D(
gl.TEXTURE_2D,
0, // level
0, // xoffset
0, // yoffset
element // pass DOM element
);
Atributos do Canvas e elementos filhos drawable
Estamos atualizando a forma como você prepara o canvas para renderizar HTML.
Renomear layoutsubtree como content="drawable"
Para se alinhar às convenções de nomenclatura de atributos HTML, o atributo booleano layoutsubtree
no elemento <canvas> foi renomeado como content="drawable". Consulte
WICG Issue #169 para
uma discussão detalhada.
O atributo drawable para descendentes
Antes, apenas os filhos diretos de um canvas eram renderizados. Para oferecer suporte a hierarquias DOM complexas sem achatar a marcação, agora é possível adicionar explicitamente o atributo drawable a qualquer elemento descendente que você pretende desenhar de forma independente:
<!-- BEFORE -->
<canvas layoutsubtree>
<div id="badge">
<h2>Player Stats</h2>
<button id="actionBtn">Equip</button>
</div>
</canvas>
<!-- AFTER (Chrome 155+) -->
<canvas content="drawable">
<!-- Subtree A (Body and heading) -->
<div id="badge" drawable>
<h2>Player Stats</h2>
<!-- Subtree B (Excluded from Subtree A; drawn and animated independently) -->
<button id="actionBtn" drawable>Equip</button>
</div>
</canvas>
Um elemento com drawable captura toda a subárvore exceto os descendentes aninhados que também especificam drawable. Isso permite que contêineres principais e elementos filhos, como botões, sejam animados e desenhados em chamadas de desenho separadas.
Sincronização de teste de ocorrência
Antes, os desenvolvedores precisavam capturar o valor de retorno dos métodos de desenho e
transmiti-lo para element.style.transform. O método drawElementImage() agora retorna
void, e a sincronização depende do contexto.
Contextos 2D: sincronização automática
No canvas 2D, o navegador sincroniza automaticamente os limites de teste de ocorrência do DOM e os anéis de foco do leitor de tela com as coordenadas desenhadas. Você pode remover todo o encadeamento de transformação manual:
// BEFORE
const transform = ctx.drawElementImage(element, x, y);
element.style.transform = transform.toString();
// AFTER (Chrome 155+)
ctx.drawElementImage(element, x, y);
// No style update needed: position & hit-testing sync automatically
Para gerenciar a geometria manualmente em 2D, transmita { preserveElementGeometry: true } para
drawElementImage() e aplique transformações usando canvas.updateElementGeometry().
Contextos 3D: explícito updateElementGeometry()
Para sincronizar o DOM em contextos WebGL ou WebGPU, crie uma transformação DOMMatrix
e transmita-a para canvas.updateElementGeometry():
canvas.addEventListener('paint', () => {
// 1. Upload element snapshot to GPU texture
device.queue.drawElementImageToTexture({ source: element }, { texture });
// 2. Render 3D scene...
drawScene();
// 3. Construct transform matrix as needed
const canvasTransform = new DOMMatrix().translate(x, y);
// 4. Update DOM hit-testing and accessibility bounding boxes
canvas.updateElementGeometry(element, { canvasTransform });
});
Compatibilidade entre versões
Se o aplicativo precisar oferecer suporte a usuários em várias versões do Chrome durante o período de transição, verifique a presença dos métodos atualizados nos protótipos e ajuste a implementação de acordo.
Verifique se você definiu os atributos de elemento <canvas> corretos para cada versão
do navegador:
// ============================================================================
// Cross-version canvas markup
// ============================================================================
if (!canvas.hasAttribute('content') && !canvas.hasAttribute('layoutsubtree')) {
if ('content' in HTMLCanvasElement.prototype) {
// Chrome 155+ (New standardized attribute)
canvas.setAttribute('content', 'drawable');
} else {
// Chrome < 155 (Legacy boolean attribute)
canvas.setAttribute('layoutsubtree', '');
}
}
Para contexto 2D, inspecione o valor de retorno de drawElementImage() e verifique os atributos do protótipo de tela para oferecer suporte às duas versões:
// ============================================================================
// Cross-version 2D drawing & synchronization
// ============================================================================
function draw2DElement(ctx, element, x, y) {
// Execute the 2D draw call
const result = ctx.drawElementImage(element, x, y);
if (result !== undefined && typeof result?.toString === 'function') {
// Chrome < 155: drawElementImage returned a DOMMatrix
// Requires manual transform piping to align DOM hit-test bounds:
element.style.transform = result.toString();
} else {
// Chrome 155+: drawElementImage returns void / undefined
// The browser automatically aligns DOM positioning, clicks, and a11y bounds!
// If you previously had element.style.transform logic here, clear it:
if (element.style.transform) {
element.style.transform = "";
}
}
}
Para WebGPU, use a assinatura e o nome do método compatíveis:
// ============================================================================
// WebGPU cross-version support
// ============================================================================
if ('drawElementImageToTexture' in GPUQueue.prototype) {
// Chrome 155+ (Latest implementation)
queue.drawElementImageToTexture(
{ source: element, sourceX: 0, sourceY: 0, sourceWidth: w, sourceHeight: h },
{ texture: myTexture, size: { width: w, height: h } }
);
} else if (typeof queue.copyElementImageToTexture === "function") {
// Chrome 150 - 154: Try the Chrome 150+ dictionary signature first,
// then fall back to the Chrome < 150 signature
try {
queue.copyElementImageToTexture(
{ source: element },
{ destination: { texture: myTexture }, width: w, height: h }
);
} catch (err) {
// Chrome < 150
queue.copyElementImageToTexture(element, { texture: myTexture });
}
}
Verifique a compatibilidade do navegador com a WebGL:
// ============================================================================
// WebGL cross-version support
// ============================================================================
if ('texElementSubImage2D' in WebGL2RenderingContext.prototype) {
// Chrome 155+ (Explicit pre-allocation + sub-image)
gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, element);
} else if (gl.texElementImage2D && gl.texElementImage2D.length === 3) {
// Chrome 150-154 (3-argument signature)
gl.texElementImage2D(gl.TEXTURE_2D, gl.RGBA8, element);
} else if (gl.texElementImage2D) {
// Chrome < 150 (6-argument signature)
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, element);
}
Extensão do teste de origem
O teste de origem do HTML no Canvas foi estendido até o Chrome 160. Renove seu token de teste de origem para continuar oferecendo o recurso HTML no Canvas aos usuários. Consulte o artigo sobre o teste de origem para mais detalhes.
Envie feedback
Informe como essas atualizações funcionam nos seus aplicativos registrando um problema no repositório do WICG no GitHub. Inscreva-se na newsletter para desenvolvedores e fique por dentro das novidades sobre HTML-in-Canvas.