在 WebGL 畫布中轉譯 HTML,即可將即時 DOM 子樹狀結構做為動態 GPU 紋理、將著色器效果套用至即時 DOM,以及將互動式 HTML 整合至 3D 場景。
本指南說明如何將 HTML 算繪至 WebGL 畫布。如要進一步瞭解 HTML-in-Canvas,請參閱「API 總覽」。
如要在 WebGL 畫布中算繪 HTML,請按照下列步驟操作:
- 準備畫布。以宣告方式設定正確的屬性,讓瀏覽器知道應將 HTML 轉譯為畫布。這項設定在 2D、WebGL 和 WebGPU 環境中都相同。瞭解如何設定畫布,以及如何宣告要獨立繪製的 HTML 元素。
- 將 HTML 套用為紋理:
- 預先分配 GPU 紋理儲存空間。
- 將 DOM 快照上傳為紋理。
- 同步處理 DOM 和像素。
將 HTML 元素繪製為 WebGL 紋理
在 WebGL 中,HTML 元素會使用 texElementSubImage2D(),以 2D 紋理的形式上傳至 GPU。
預先分配紋理儲存空間
2D 畫布會自動管理備份記憶體,但 WebGL 內容需要明確的記憶體管理。
上傳 DOM 像素前,您必須預先分配 GPU 記憶體。這樣一來,瀏覽器就能快速更新子圖片,不必觸發昂貴的 GPU 記憶體重新分配作業。
使用所選大小參數預先分配緩衝區,並將 srcData 設為 null,如下所示:
// 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;
}
如要讓紋理符合 HTML 元素的自然版面配置大小,請使用 canvas.getElementImageDefaultSize() 擷取預設大小 (以畫布格線像素為單位):
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
);
將 DOM 快照上傳為紋理
下一步是在算繪迴圈或 paint 事件監聽器中,將 HTML 元素上傳至紋理。
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()`
);
// ...
};
同步處理幾何圖形
與 2D 情境不同,瀏覽器無法自動推斷紋理在 3D 情境中顯示在畫面上的位置。您必須手動將 DOM 點擊測試和無障礙定界框與 3D 網格對齊。
如要這麼做,請建構 DOMMatrix,將元素的邊框方塊對應至轉譯的畫布像素位置,然後使用 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,
});
});
後續步驟
- 瞭解 HTML-in-Canvas 的核心技術概念。
- 瞭解如何在 2D 情境中算繪 HTML。
- 瞭解如何在 WebGPU Canvas 中算繪 HTML。
- 瞭解備用策略。
- 歡迎在 GitHub 回報問題,分享您的意見。
- 請參閱社群提供的 HTML-in-Canvas 示範集合。