Chrome 150 và 155 đã giới thiệu một số thay đổi đối với HTML-in-Canvas API. Những thay đổi này được thực hiện dựa trên ý kiến phản hồi của cộng đồng và các tiêu chuẩn nhằm cải thiện tính gọn nhẹ của API. Bài đăng này trên blog trình bày những thay đổi và cách bạn có thể đảm bảo rằng chế độ triển khai của mình luôn được cập nhật những thay đổi đó.
Các bản cập nhật bao gồm việc đổi tên các thuộc tính và phương thức, yêu cầu phân bổ trước bộ nhớ kết cấu rõ ràng và cập nhật các mô hình đồng bộ hoá DOM. Ngoài ra, Thử nghiệm ban đầu về HTML trong Canvas hiện đã được mở rộng. Sau đây là nội dung giải thích về những thay đổi, lý do và cách cập nhật toàn bộ mã nguồn của bạn.
Thay đổi trong Chrome 150
Trong Chrome 150, chúng tôi đã thay đổi chữ ký đối số trên WebGL và WebGPU để loại bỏ sự dư thừa và phù hợp với các quy ước đồ hoạ hiện đại.
WebGL
Chúng tôi đã xoá các đối số thừa (level, srcFormat, destType) khỏi texElementImage2D (xem cuộc thảo luận về WebGL để biết thêm thông tin chi tiết):
// 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
Chúng tôi đã chuẩn hoá các tham số copyElementImageToTexture thành các đối tượng sourceDict và destDict (xem 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);
Thay đổi trong Chrome 155
Chrome 155 có thêm các điểm tinh chỉnh dựa trên ý kiến phản hồi của nhà phát triển. Chúng tôi đang cập nhật tên phương thức, yêu cầu phân bổ trước bộ nhớ kết cấu rõ ràng, hỗ trợ các cây con DOM phức tạp và tách rời quá trình đồng bộ hoá DOM.
WebGPU
Phương thức WebGPU đã được đổi tên từ copyElementImageToTexture thành drawElementImageToTexture. Cùng với việc đổi tên, toạ độ tham số được mở rộng (sourceX, sourceWidth) và ranh giới đích đến được nhóm thành một đối tượng size (GPUExtent3D) tiêu chuẩn:
// 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
Có 3 khía cạnh đối với các thay đổi trong WebGL cho HTML trong Canvas:
- Tên phương thức thay đổi từ
texElementImage2DthànhtexElementSubImage2D.` - Chữ ký phương thức thay đổi; quan trọng nhất là chữ ký này thêm các đối số
xoffsetvàyoffsetđể cho phép chỉ định một độ lệch vào kết cấu đích mà tại đó vẽ phần tử (không bắt buộc). - Phương thức này không còn tự động phân bổ bộ nhớ đệm cho kết cấu đích nữa; bạn phải phân bổ rõ ràng bộ nhớ trước khi sử dụng API. Điều này tương tự như hành vi của
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
);
Thuộc tính canvas và các phần tử con có thể vẽ
Chúng tôi đang cập nhật cách bạn chuẩn bị canvas để hiển thị HTML.
Đổi tên layoutsubtree thành content="drawable"
Để tuân theo quy ước đặt tên thuộc tính HTML, thuộc tính boolean layoutsubtree trên phần tử <canvas> đã được đổi tên thành content="drawable" (xem Vấn đề 169 của WICG để biết thông tin thảo luận chi tiết).
Thuộc tính drawable cho các phần tử con
Trước đây, chỉ các thành phần con trực tiếp của một canvas mới được kết xuất. Để hỗ trợ các hệ phân cấp DOM phức tạp mà không cần làm phẳng mã đánh dấu, giờ đây, bạn có thể thêm thuộc tính drawable một cách rõ ràng vào bất kỳ phần tử con nào mà bạn dự định vẽ một cách độc lập:
<!-- 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>
Một phần tử có drawable sẽ nắm bắt toàn bộ cây con của phần tử đó ngoại trừ những thành phần con lồng nhau cũng chỉ định drawable. Điều này cho phép các vùng chứa mẹ và các phần tử con (chẳng hạn như nút) được tạo ảnh động và vẽ trong các lệnh vẽ riêng biệt.
Đồng bộ hoá kiểm thử lượt nhấn
Trước đây, nhà phát triển phải ghi lại giá trị trả về từ các phương thức vẽ và chuyển giá trị đó vào element.style.transform. Phương thức drawElementImage() hiện trả về giá trị rỗng và quá trình đồng bộ hoá phụ thuộc vào ngữ cảnh.
2D Contexts: Automatic Synchronization (Ngữ cảnh 2D: Tự động đồng bộ hoá)
Trong canvas 2D, trình duyệt sẽ tự động đồng bộ hoá ranh giới kiểm tra lượt truy cập DOM và vòng tiêu điểm của trình đọc màn hình với các toạ độ được vẽ. Bạn có thể xoá tất cả các đường ống biến đổi thủ công:
// 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
Để quản lý hình học theo cách thủ công ở chế độ 2D, hãy truyền { preserveElementGeometry: true } đến drawElementImage() và áp dụng các phép biến đổi bằng cách sử dụng canvas.updateElementGeometry().
Bối cảnh 3D: Phản cảm updateElementGeometry()
Để đồng bộ hoá DOM trong ngữ cảnh WebGL hoặc WebGPU, hãy tạo một phép biến đổi DOMMatrix và truyền phép biến đổi đó đến 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 });
});
Khả năng tương thích giữa các phiên bản
Nếu ứng dụng của bạn phải hỗ trợ người dùng trên nhiều phiên bản Chrome trong thời gian chuyển đổi, hãy kiểm tra sự hiện diện của các phương thức đã cập nhật trên nguyên mẫu của họ và điều chỉnh việc triển khai cho phù hợp.
Đảm bảo bạn đặt đúng thuộc tính phần tử <canvas> cho mọi phiên bản trình duyệt:
// ============================================================================
// 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', '');
}
}
Đối với ngữ cảnh 2D, hãy kiểm tra giá trị trả về của drawElementImage() và kiểm tra các thuộc tính nguyên mẫu canvas để hỗ trợ cả hai phiên bản:
// ============================================================================
// 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 = "";
}
}
}
Đối với WebGPU, hãy dùng chữ ký và tên phương thức tương thích:
// ============================================================================
// 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 });
}
}
Hãy nhớ kiểm tra khả năng tương thích của trình duyệt với 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);
}
Tiện ích dùng thử nguồn
Bản dùng thử theo nguyên gốc HTML trong Canvas đã được mở rộng đến Chrome 160. Nhớ gia hạn mã thông báo dùng thử nguồn gốc để tiếp tục cung cấp tính năng HTML trong Canvas cho người dùng. Hãy xem bài viết về Thử nghiệm nguồn gốc để biết thêm thông tin.
Chia sẻ ý kiến phản hồi của bạn
Hãy cho chúng tôi biết những điểm cập nhật này hoạt động như thế nào trong các ứng dụng của bạn bằng cách báo cáo vấn đề trên kho lưu trữ WICG GitHub. Đăng ký nhận bản tin dành cho nhà phát triển để luôn nắm bắt mọi tin tức về HTML trong Canvas.