WebGPU 畫布中的 HTML

在 WebGPU 畫布中算繪 HTML,可享有 WebGL 畫布中 HTML 的所有優點,例如將著色器效果套用至 DOM,或將互動式 HTML 整合至 3D 場景。不過,WebGPU 的優勢遠勝 WebGL。

本指南說明如何將 HTML 轉譯為 WebGPU 畫布。如要進一步瞭解 Canvas 中的 HTML,請參閱 API 總覽。

在 WebGPU 畫布中轉譯 HTML 需要執行下列步驟:

  1. 準備畫布。以宣告方式設定正確的屬性,讓瀏覽器知道應將 HTML 轉譯為畫布。這項設定在 2D、WebGL 和 WebGPU 環境中都相同。瞭解如何設定畫布,以及如何宣告要獨立繪製的 HTML 元素。
  2. 將 HTML 套用為紋理:
    1. 預先分配 GPU 紋理儲存空間。
    2. 將 DOM 快照上傳為紋理。
  3. 同步處理 DOM 和像素。

將 HTML 元素繪製為 WebGPU 紋理

在 WebGPU 中,HTML 元素會使用 drawElementImageToTexture() 上傳至 GPU 做為 2D 紋理。

預先分配紋理儲存空間

WebGPU 需要明確的記憶體配置。將 DOM 像素複製到紋理前,必須使用 device.createTexture() 方法保留 GPU VRAM:

const context = canvas.getContext("webgpu");

// Pre-allocate the texture size as needed
// For example: HTML element CSS size × devicePixelRatio
const size = canvas.getElementImageDefaultSize(myElement);
const myElementWidth = Math.ceil(size.width);
const myElementHeight = Math.ceil(size.height);

// Pre-allocate the GPUTexture backing (run once during setup)
const myTexture = device.createTexture({
  size: {
    width: myElementWidth,
    height: myElementHeight,
    depthOrArrayLayers: 1, // optional (defaults to 1)
  },
  format: "rgba8unorm",
  usage:
    GPUTextureUsage.TEXTURE_BINDING |
    GPUTextureUsage.COPY_DST |
    GPUTextureUsage.RENDER_ATTACHMENT,
});

繪製 HTML 元素圖片

在 WebGPU 中,drawElementImageToTexture() 方法會繪製 HTML 元素的快照。這個方法會採用兩個字典物件:source 和 destination。

source 定義要光柵化的來源 DOM 元素,以及選用的子矩形裁剪座標,讓您只將元素的一部分複製到紋理,而不是整個 DOM 節點:

屬性 類型 說明
必須填寫「source」 Element 或 ElementImage 要繪製到紋理中的目標 DOM 元素 (或預先擷取的 ElementImage 執行個體)。
sourceX 選填 (四個都填或都不填) 浮點數 水平 X 座標偏移 (以 CSS 像素為單位),定義來源子矩形切片的左上角。
sourceY 選填 (四個都填或都不填) 浮點數 垂直 Y 座標偏移 (以 CSS 像素為單位),定義來源子矩形切片的左上角。
sourceWidth 選填 (四個都填或都不填) 浮點數 要裁剪及光柵化的來源子矩形寬度 (以 CSS 像素為單位)。
sourceHeight 選填 (四個都填或都不填) 浮點數 要裁剪及光柵化的來源子矩形高度 (以 CSS 像素為單位)。

destination 定義接收算繪 DOM 快照的目標 GPU 紋理,以及選用的目的地偏移、Mip 層級和複製維度,將像素放入紋理的特定區域:

屬性 類型 說明
必須填寫「texture」 GPUTexture 預先分配的目的地 WebGPU 紋理,會接收轉譯的 DOM 像素快照。
size 個選答題 GPUExtent3D 在目標紋理中縮放及光柵化元素的寬度和高度 (以實體紋理像素為單位,例如 { width, height })。
origin 個選答題 GPUOrigin3D 紋理內 3D 目的地偏移 (x、y、z),光柵化像素會寫入此處。預設值為 [0, 0, 0]。
mipLevel 個選答題 GPUIntegerCoordinate 要寫入的目的地紋理特定 mipmap 層級。預設值為 0。
aspect 個選答題 GPUTextureAspect 指定要複製到哪個紋理層面 ("all"、"stencil-only" 或 "depth-only")。預設值為 "all"。
colorSpace 個選答題 PredefinedColorSpace 上傳時用於色彩轉換的目的地色域 (例如 "srgb"、"display-p3")。預設值為 "srgb"。
premultipliedAlpha 個選答題 布林值 上傳期間是否應將 Alpha 預乘至色彩通道。預設值為 false。

呼叫 drawElementImageToTexture() 方法,將元素上傳至 paint 事件中的紋理:

canvas.addEventListener("paint", (event) => {
  const sourceDict = {
    source: myElement,
    // Optional sub-rectangle crop in CSS pixels
    // (defaults to full element if omitted):
    sourceX: 0,
    sourceY: 0,
    sourceWidth: myElement.offsetWidth,
    sourceHeight: myElement.offsetHeight,
  };

  const destDict = {
    texture: myTexture,
    size: { width: myElementWidth, height: myElementHeight },
  };

  // Upload the latest DOM snapshot into the pre-allocated GPUTexture
  device.queue.drawElementImageToTexture(sourceDict, destDict);
});

同步處理幾何圖形

如要讓 DOM 點擊測試和無障礙焦點環與轉譯的畫布輸出內容對齊,請建構 DOMMatrix 轉換,並使用 updateElementGeometry() 方法套用:

// Construct a transform as needed in CSS pixels
// relative to the canvas, for example:
// 1/8 of the canvas width from the left edge, vertically centered.
// scaleX, scaleY: canvas grid pixels per CSS pixel (from resize observer)
const canvasCssWidth = canvas.width / scaleX;
const canvasCssHeight = canvas.height / scaleY;
const elementCssHeight = canvas.getElementImageDefaultSize(myElement).height / scaleY;
const x = canvasCssWidth / 8;
const y = (canvasCssHeight - elementCssHeight) / 2;
const transform = new DOMMatrix().translate(x, y);

// Synchronize the element's DOM hit-testing and accessibility geometry
canvas.updateElementGeometry(myElement, { canvasTransform: transform });

後續步驟