W Chrome 150 i 155 wprowadziliśmy kilka zmian w interfejsie HTML-in-Canvas API. Te zmiany zostały wprowadzone w odpowiedzi na opinie społeczności i organizacji normalizacyjnych, aby zwiększyć ergonomię interfejsu API. W tym poście na blogu opisujemy te zmiany i wyjaśniamy, jak zadbać o to, aby Twoja implementacja była z nimi zgodna.
Aktualizacje obejmują zmianę nazw atrybutów i metod, wymaganie jawnej wstępnej alokacji pamięci tekstury oraz aktualizację modeli synchronizacji DOM. Dodatkowo okres próbny HTML-in-Canvas został przedłużony. Poniżej znajdziesz wyjaśnienie, co się zmienia, dlaczego i jak zaktualizować bazę kodu.
Zmiany w Chrome 150
W Chrome 150 zmieniliśmy sygnatury argumentów w WebGL i WebGPU, aby wyeliminować nadmiarowość i dostosować je do nowoczesnych konwencji graficznych.
WebGL
Usunęliśmy z funkcji texElementImage2D zbędne argumenty (level, srcFormat, destType) (więcej informacji znajdziesz w dyskusji na temat WebGL):
// 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
Ujednoliciliśmy copyElementImageToTextureparametry w sourceDict i destDict obiektach (zobacz 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);
Zmiany w Chrome 155
W Chrome 155 wprowadzamy dalsze ulepszenia na podstawie opinii programistów. Aktualizujemy nazwy metod, wymagamy jawnej wstępnej alokacji pamięci tekstur, obsługujemy złożone poddrzewa DOM i rozdzielamy synchronizację DOM.
WebGPU
Nazwę metody WebGPU zmieniono z copyElementImageToTexture na drawElementImageToTexture. Oprócz zmiany nazwy rozszerzono współrzędne parametrów (sourceX, sourceWidth), a granice miejsca docelowego zgrupowano w standardowym obiekcie 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
Zmiany w WebGL w przypadku HTML w obszarze rysowania obejmują 3 aspekty:
- Nazwa metody zmienia się z
texElementImage2DnatexElementSubImage2D. - Zmienia się sygnatura metody, a najważniejsze jest to, że dodaje ona argumenty
xoffsetiyoffset, które opcjonalnie umożliwiają określenie przesunięcia w teksturze docelowej, w której ma być rysowany element. - Metoda nie przydziela już automatycznie pamięci buforowanej dla tekstury docelowej. Pamięć musi zostać jawnie przydzielona przed użyciem interfejsu API. Działa to podobnie jak w przypadku
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
);
Atrybuty elementu Canvas i elementy podrzędne, które można rysować
Zmieniamy sposób przygotowywania obszaru roboczego do renderowania kodu HTML.
Zmieniam nazwę layoutsubtree na content="drawable"
Aby zachować zgodność z konwencjami nazewnictwa atrybutów HTML, atrybut logiczny layoutsubtreeelementu <canvas> został zmieniony na content="drawable" (szczegółowe omówienie znajdziesz w WICG Issue #169).
Atrybut drawable dla elementów podrzędnych
Wcześniej renderowane były tylko bezpośrednie elementy podrzędne obszaru roboczego. Aby obsługiwać złożone hierarchie DOM bez spłaszczania znaczników, możesz teraz jawnie dodać atrybut drawable do dowolnego elementu podrzędnego, który chcesz rysować niezależnie:
<!-- 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>
Element z atrybutem drawable obejmuje cały poddrzewo z wyjątkiem zagnieżdżonych elementów podrzędnych, które również określają drawable. Dzięki temu kontenery nadrzędne i elementy podrzędne, takie jak przyciski, mogą być animowane i rysowane w osobnych wywołaniach rysowania.
Synchronizacja testowania kliknięć
Wcześniej deweloperzy musieli przechwytywać wartość zwracaną przez metody rysowania i przekazywać ją do funkcji element.style.transform. Metoda drawElementImage() zwraca teraz wartość null, a synchronizacja zależy od kontekstu.
Konteksty 2D: automatyczna synchronizacja
W przypadku płótna 2D przeglądarka automatycznie synchronizuje granice testu trafienia DOM i pierścienie fokusu czytnika ekranu z narysowanymi współrzędnymi. Możesz usunąć wszystkie ręczne przekształcenia:
// 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
Aby ręcznie zarządzać geometrią w 2D, przekaż { preserveElementGeometry: true } do
drawElementImage() i zastosuj przekształcenia za pomocą canvas.updateElementGeometry().
Konteksty 3D: jawne updateElementGeometry()
Aby zsynchronizować DOM w kontekstach WebGL lub WebGPU, utwórz DOMMatrix transform
i przekaż go do 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 });
});
Zgodność między wersjami
Jeśli w okresie przejściowym aplikacja musi obsługiwać użytkowników korzystających z różnych wersji Chrome, sprawdź, czy w ich prototypach są dostępne zaktualizowane metody, i odpowiednio dostosuj implementację.
Upewnij się, że dla każdej wersji przeglądarki ustawiasz prawidłowe atrybuty elementu <canvas>:
// ============================================================================
// 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', '');
}
}
W przypadku kontekstu 2D sprawdź wartość zwracaną przez drawElementImage() i atrybuty prototypu elementu canvas, aby obsługiwać obie wersje:
// ============================================================================
// 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 = "";
}
}
}
W przypadku WebGPU użyj zgodnej sygnatury i nazwy metody:
// ============================================================================
// 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 });
}
}
Sprawdź, czy przeglądarka jest zgodna z 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);
}
Rozszerzenie okresu próbnego
Testowanie origin HTML-in-Canvas zostało przedłużone do Chrome 160. Aby nadal oferować użytkownikom funkcję HTML w Canvas, odnów token testowania origin. Więcej informacji znajdziesz w artykule o testach Origin.
Prześlij opinię
Daj nam znać, jak te aktualizacje działają w Twoich aplikacjach, zgłaszając problem w repozytorium WICG na GitHubie. Zasubskrybuj newsletter dla deweloperów, aby być na bieżąco z informacjami o HTML w Canvas.