在 WebGPU 画布中渲染 HTML 可提供在 WebGL 画布中渲染 HTML 的所有优势,例如将着色器效果应用于 DOM 或将交互式 HTML 集成到 3D 场景中。不过,与 WebGL 相比,WebGPU 具有显著优势。
本指南演示了如何将 HTML 渲染到 WebGPU 画布中。如需详细了解画布中的 HTML,请参阅 API 概览。
在 WebGPU 画布中渲染 HTML 需要执行以下步骤:
- 准备画布。以声明方式设置正确的属性,让浏览器知道它应该将 HTML 渲染到画布中。此设置在 2D、WebGL 和 WebGPU 上下文中完全相同。了解如何设置画布以及如何声明要独立绘制的 HTML 元素。
- 将 HTML 作为纹理应用:
- 预分配 GPU 纹理存储空间。
- 将 DOM 快照作为纹理上传。
- 同步 DOM 和像素。
将 HTML 元素绘制为 WebGPU 纹理
在 WebGPU 中,HTML 元素会使用 drawElementImageToTexture() 作为 2D 纹理上传到 GPU。
预分配纹理存储空间
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 可选(全部四个或一个都没有) |
float | 水平 X 坐标偏移量(以 CSS 像素为单位),用于定义源子矩形切片的左上角。 |
sourceY 可选(全部四个或一个都没有) |
float | 垂直 Y 坐标偏移量(以 CSS 像素为单位),用于定义源子矩形切片的左上角。 |
sourceWidth 可选(全部四个或一个都没有) |
float | 要剪裁和光栅化的源子矩形的宽度(以 CSS 像素为单位)。 |
sourceHeight 可选(全部四个或一个都没有) |
float | 要剪裁和光栅化的源子矩形的高度(以 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 });
后续步骤
- 了解 HTML-in-Canvas 背后的核心技术概念。
- 了解如何在 2D 上下文中呈现 HTML。
- 了解如何在 WebGL 画布中渲染 HTML。
- 了解回退策略。
- 在 GitHub 上提交问题,分享您的反馈。
- 查看社区提供的 HTML-in-Canvas 演示集合。