Updates voor HTML-in-Canvas: Op weg naar een betere web-API

Chrome 150 en 155 introduceerden verschillende wijzigingen in de HTML-in-Canvas API. Deze wijzigingen waren een reactie op feedback vanuit de community en de standaardisatie, met als doel de gebruiksvriendelijkheid van de API te verbeteren. In dit blogbericht worden de wijzigingen beschreven en hoe u ervoor kunt zorgen dat uw implementatie hiermee compatibel is.

De updates omvatten het hernoemen van attributen en methoden, het vereisen van expliciete geheugenvoorallocatie voor texturen en het bijwerken van DOM-synchronisatiemodellen. Daarnaast is de HTML-in-Canvas Origin Trial nu uitgebreid . Hieronder volgt een uitleg van wat er verandert, waarom en hoe u uw codebase kunt bijwerken.

Wijzigingen in Chrome 150

In Chrome 150 hebben we de argumenthandtekeningen voor WebGL en WebGPU aangepast om redundantie te elimineren en aan te sluiten bij moderne grafische conventies.

WebGL

We hebben overbodige argumenten ( level , srcFormat , destType ) verwijderd uit texElementImage2D (zie de WebGL-discussie voor meer details):

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

We hebben de parameters copyElementImageToTexture gestandaardiseerd in sourceDict en destDict -objecten (zie 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);

Wijzigingen in Chrome 155

Chrome 155 introduceert verdere verfijningen op basis van feedback van ontwikkelaars. We werken de methodenamen bij, vereisen expliciete pre-allocatie van textuurgeheugen, ondersteunen complexe DOM-substructuren en ontkoppelen DOM-synchronisatie.

WebGPU

De WebGPU-methode is hernoemd van copyElementImageToTexture naar drawElementImageToTexture . Tegelijkertijd zijn de parametercoördinaten uitgebreid ( sourceX , sourceWidth ) en zijn de bestemmingsgrenzen gegroepeerd in een object van size ( GPUExtent3D ).

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

Er zijn drie aspecten aan de wijzigingen in WebGL voor HTML-in-Canvas:

  • De naam van de methode verandert van texElementImage2D naar texElementSubImage2D .
  • De methodehandtekening verandert; de belangrijkste wijziging is de toevoeging van de argumenten xoffset en yoffset , waarmee optioneel een offset in de bestemmingstextuur kan worden opgegeven waarop het element moet worden getekend.
  • De methode wijst niet langer automatisch buffergeheugen toe voor de doeltextuur; het geheugen moet expliciet worden toegewezen voordat de API kan worden gebruikt. Dit is vergelijkbaar met het gedrag van 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
);

Canvas-attributen en tekenbare subelementen

We werken de manier bij waarop je je canvas voorbereidt om HTML weer te geven.

De naam layoutsubtree wordt gewijzigd naar content="drawable"

Om te voldoen aan de naamgevingsconventies voor HTML-attributen, is het booleaanse attribuut layoutsubtree op het <canvas> -element hernoemd naar content="drawable" (zie WICG Issue #169 voor een gedetailleerde bespreking).

Het drawable attribuut voor afstammelingen

Voorheen werden alleen directe kinderen van een canvas weergegeven. Om complexe DOM-hiërarchieën te ondersteunen zonder de markup te vereenvoudigen, kunt u nu expliciet het drawable attribuut toevoegen aan elk afstammelingselement dat u onafhankelijk wilt tekenen:

<!-- 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>

Een element met drawable legt de volledige substructuur vast, met uitzondering van geneste afstammelingen die ook de eigenschap drawable hebben. Hierdoor kunnen oudercontainers en kindelementen, zoals knoppen, in afzonderlijke `draw`-oproepen worden geanimeerd en getekend.

Hit-test synchronisatie

Voorheen moesten ontwikkelaars de retourwaarde van tekenmethoden opvangen en deze doorgeven aan element.style.transform . De drawElementImage() -methode retourneert nu void, en de synchronisatie is afhankelijk van de context.

2D-contexten: automatische synchronisatie

In een 2D-canvas synchroniseert de browser automatisch de DOM-hittestgrenzen en de focusringen van de schermlezer met de getekende coördinaten. U kunt alle handmatige transformatie-instructies verwijderen:

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

Om de geometrie in 2D handmatig te beheren, geef je { preserveElementGeometry: true } door aan drawElementImage() en pas je transformaties toe met canvas.updateElementGeometry() .

3D-contexten: Expliciete updateElementGeometry()

Om de DOM te synchroniseren in WebGL- of WebGPU-contexten, construeer je een DOMMatrix -transformatie en geef je deze door aan 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 });
});

Compatibiliteit tussen verschillende versies

Als uw applicatie tijdens de overgangsperiode gebruikers in meerdere Chrome-versies moet ondersteunen, controleer dan of de bijgewerkte methoden aanwezig zijn in de prototypes en pas uw implementatie dienovereenkomstig aan.

Zorg ervoor dat je de juiste <canvas> -elementattributen instelt voor elke browserversie:

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

Voor een 2D-context, controleer de retourwaarde van drawElementImage() en bekijk de canvas-prototype-attributen om beide versies te ondersteunen:

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

Gebruik voor WebGPU de compatibele methodehandtekening en -naam:

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

Controleer de browsercompatibiliteit met 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);
}

Origin-proefverlenging

De Origin-proefperiode voor HTML-in-Canvas is verlengd tot en met Chrome versie 160. Zorg ervoor dat u uw Origin-proeftoken verlengt om de HTML-in-Canvas-functie aan uw gebruikers te blijven aanbieden. Zie het artikel over de Origin-proefperiode voor meer informatie.

Deel je feedback

Laat ons weten hoe deze updates in uw applicaties werken door een issue aan te maken op de WICG GitHub-repository . Abonneer u op de ontwikkelaarsnieuwsbrief om op de hoogte te blijven van al het nieuws over HTML-in-Canvas.