关于 pixijs-html-source
用于在 PixiJS v8 中将 DOM 元素及其不可变快照用作纹理源的技能,面向需要在 PixiJS 中渲染浏览器元素的开发者,并涵盖实时更新和快照生命周期。
主要功能
- 01将 DOM 元素包装为纹理源
- 02支持实时 HTMLSource 更新
- 03支持不可变 ElementImageSource 快照
- 04纹理可用于 Sprite、Texture 和 Mesh
- 05说明浏览器支持检测与资源清理
适用场景
- 01在 PixiJS 场景中显示可交互 DOM 元素
- 02按需将变化中的元素重新绘制到纹理
- 03为转场或拖尾效果保留静态快照
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-html-source安装后在 Claude Code 中输入/pixijs-html-source确认已加载。
详细介绍
HTMLSource 和 ElementImageSource 可将 DOM 元素转换为 TextureSource,供所有原本可使用普通纹理的地方使用:例如应用于 Sprite、作为 Texture 帧,或映射到 Mesh 上。HTMLSource 会将实时元素的像素同步到 GPU(该元素在浏览器中仍可编辑和点击);ElementImageSource 则封装不可变快照,不会重新绘制。两者都位于 pixi.js/html-source 中;从该路径导入会注册它们的扩展。
这些来源依赖实验性的 HTML-in-Canvas 浏览器提案,在 PixiJS v8 中标记为 EXPERIMENTAL。必须启用浏览器 API,否则纹理上传器会在首次渲染时抛出异常;依赖该 API 前,请使用
canvas.requestPaint进行特性检测。该 API 在不同次要版本间可能会发生变化。
假设你熟悉 pixijs-scene-sprite 和纹理。这些是纹理来源,而不是显示对象:请将它们封装在 Sprite(或 Texture/Mesh)中,才能显示在屏幕上。Web Worker 中不可用;Worker 没有可供捕获的 DOM。
快速开始
import { Application, Sprite } from "pixi.js";
import { HTMLSource } from "pixi.js/html-source";
const app = new Application();
await app.init({ resizeTo: window });
document.body.appendChild(app.canvas);
// The element must be a direct child of the Pixi canvas.
const form = document.createElement("form");
form.innerHTML = '<input value="still editable" />';
app.canvas.appendChild(form);
// Render the live form as a sprite. It stays interactive in the browser.
const source = new HTMLSource({ resource: form });
const sprite = Sprite.from(source);
sprite.anchor.set(0.5);
sprite.position.set(app.screen.width / 2, app.screen.height / 2);
app.stage.addChild(sprite);
相关技能: pixijs-scene-sprite(显示纹理)、pixijs-scene-mesh(映射到几何体上,PerspectiveMesh)、pixijs-scene-dom-container(相反的做法:在画布上方、GPU 流程之外叠加 HTML)、pixijs-assets(纹理来源与加载器/缓存)、pixijs-environments(Web Worker 中没有 DOM)。
构造函数选项
这两种来源都扩展了 TextureSource,因此所有 TextureSourceOptions(resolution、scaleMode、addressMode、label 等)均有效。每种来源都必须提供 resource。
HTMLSourceOptions(实时元素):
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
resource | Element | — | 必填。要渲染的实时 DOM 元素。必须是所属画布的直接子元素,否则构造函数会抛出异常。 |
canvas | HTMLSourceCanvas | — | 拥有该元素布局子树的画布。如果元素是画布的直接子元素,则会从 resource.parentElement 推断;无法推断时请传入此项。 |
autoLayout | boolean | true | 在所属画布上设置 layoutsubtree 属性。浏览器仅在存在此属性时才会对画布子元素进行布局和绘制。如果你自行编写 <canvas layoutsubtree>,请设为 false。 |
autoUpdate | boolean | true | 监听画布的 paint 事件,并在元素重新绘制时重新上传。对于仅捕获一次的静态纹理,请设为 false。 |
autoRequestPaint | boolean | true | 构造完成后请求一次初始绘制。对于连续动画,请设为 false,并在每一帧自行调用 source.requestPaint()。 |
ElementImageSourceOptions(不可变快照):
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
resource | ElementImage | — | 必填。来自 canvas.captureElementImage(element) 的快照。 |
autoClose | boolean | false | 销毁来源时调用 snapshot.close()。如果此快照与其他来源共享,请保持为 false,否则可能会在释放后继续使用。 |
通过静态 HTMLSource.defaultOptions 对象更改所有 HTMLSource 的默认值,例如 HTMLSource.defaultOptions.autoUpdate = false。
核心模式
设置和副作用导入
import "pixi.js/html-source";
import { HTMLSource, ElementImageSource } from "pixi.js/html-source";
pixi.js/html-source 会调用 extensions.add(...),以注册 HTMLSource、ElementImageSource 及其 WebGL/WebGPU 上传器。没有它,渲染器就没有 'html' 上传器,这些来源也不会渲染。这些类从 pixi.js/html-source 导出,而非 pixi.js。
从 pixi.js/html-source 导入具名导出也会触发此副作用,因此只有在不从该路径导入其他内容时,才需要单独使用 import 'pixi.js/html-source'。
功能检测和浏览器支持
HTML-in-Canvas API 受浏览器标志控制。使用前请检测功能是否可用:
import type { HTMLSourceCanvas } from "pixi.js/html-source";
const canvas = app.canvas as HTMLSourceCanvas;
if (canvas.requestPaint) {
// HTML-in-Canvas is available.
}
将 app.canvas 转换为 HTMLSourceCanvas,以使用带类型的 requestPaint 和 captureElementImage 成员。当浏览器不支持该 API 时,source.requestPaint() 会返回 false;如果该 API 被禁用,纹理上传器会在首次渲染时抛出异常。
缺少该 API 时,首次上传会在 WebGL 中抛出 [HTMLSource] WebGLRenderingContext.texElementImage2D is not available...,或在 WebGPU 中抛出 [HTMLSource] GPUQueue.copyElementImageToTexture is not available...。
使用 HTMLSource 创建实时元素
const form = document.createElement("form");
app.canvas.appendChild(form); // direct child of the canvas
const source = new HTMLSource({ resource: form });
const sprite = Sprite.from(source);
该元素必须是渲染器 <canvas> 的直接子元素;源码会从 resource.parentElement 推断所属的 canvas(也可以传入 canvas)。使用默认设置时,它会在 canvas 上设置 layoutsubtree,监听 canvas 的 paint 事件,并请求一次初始绘制。首次绘制完成前,source.isReady 为 false,之后变为 true。当 autoUpdate 为 false 时,该属性会立即为 true。resourceWidth/resourceHeight 会返回元素的实际像素尺寸(offsetWidth/offsetHeight)。
使用 requestPaint 持续动画
const source = new HTMLSource({ resource: clock, autoRequestPaint: false });
const sprite = Sprite.from(source);
app.ticker.add(() => {
clock.textContent = new Date().toLocaleTimeString();
source.requestPaint(); // re-snapshot the DOM this frame
});
浏览器只会按需重绘 canvas 子元素。对于内容逐帧变化的元素,请设置 autoRequestPaint: false,并在自己的计时器中调用 source.requestPaint(),按计划触发重绘。
使用 ElementImageSource 创建不可变快照
import { ElementImageSource } from "pixi.js/html-source";
import type { HTMLSourceCanvas } from "pixi.js/html-source";
const canvas = app.canvas as HTMLSourceCanvas;
const snapshot = canvas.captureElementImage!(element);
const source = new ElementImageSource({ resource: snapshot, autoClose: true });
const sprite = Sprite.from(source);
captureElementImage() 会将元素当前的像素冻结为不可变的 ElementImage。它没有所属的 canvas、没有 paint 监听器,也没有重绘生命周期,因此 source 在构造完成后立即就绪。需要创建一个独立于元素生命周期的冻结副本,或需要传递快照时(例如转场、“碎裂”或拖尾效果),可使用此方法。完成后调用 snapshot.close() 释放快照,或传入 autoClose: true,使 source 在 destroy() 时关闭快照。
在精灵、纹理或网格上使用 source
两种 source 都是常规的 TextureSource。可使用 Sprite.from(source) / Texture.from(source) 将它们封装起来,将其分帧或切片为子纹理,也可以将它们映射到网格上:
import { Rectangle, Texture } from "pixi.js";
// A 64x64 slice of the rendered element.
const chunk = new Texture({
source,
frame: new Rectangle(0, 0, 64, 64),
});
// Mapped onto geometry (e.g. a perspective warp).
const mesh = new PerspectiveMesh({ texture: Texture.from(source) /* ... */ });
自动检测与优先级
// Resolves to an HTMLSource (element) or ElementImageSource (snapshot) only as a last resort.
const sprite = Sprite.from(elementAlreadyInTheCanvas);
传入 Texture.from/Sprite.from 的通用 HTML 元素或 ElementImage,会以最低纹理 source 优先级(-10)解析为这些 source,因此只有在其他内置 source 都无法处理该资源时,才会由它们接管。系统会特意拒绝图像、视频和 canvas 元素;它们有专用且更快的 source。需要设置选项(autoUpdate、autoClose)或处理 SVG 等非 HTML 元素时,请显式构造 source。
常见错误
[HIGH] 从 pixi.js 而非 pixi.js/html-source 导入
错误:
import { HTMLSource } from "pixi.js"; // not exported from the core entry
正确:
import { HTMLSource } from "pixi.js/html-source";
HTMLSource、ElementImageSource 和 'html' 上传器仅位于 pixi.js/html-source 中。从该路径导入任何内容都会注册相关扩展;此模块标记为有副作用,因此即使只导入一个类,打包工具也会保留该注册代码。
[HIGH] 假设浏览器 API 已启用
HTML-in-Canvas 提案默认未随浏览器发布。如果 API 已禁用,上传器会在首次渲染时抛出错误。请先检测功能:
const canvas = app.canvas as HTMLSourceCanvas;
if (!canvas.requestPaint) {
// Fall back to a static image, DOMContainer overlay, or a message.
}
[MEDIUM] 元素不是 canvas 的直接子元素
document.body.appendChild(form); // wrong parent
const source = new HTMLSource({ resource: form }); // throws
HTMLSource 要求元素必须是所属 canvas 的直接子元素(app.canvas.appendChild(form)),否则会抛出错误。请先将元素追加到 canvas,再构造 source,或传入 canvas 选项。
[MEDIUM] 期望实时元素在没有 requestPaint 时更新
不执行动画的元素会在浏览器的 paint 事件中自动更新(autoUpdate: true)。逐帧变化的内容不会重新上传,除非有操作触发绘制;请每帧调用 source.requestPaint() 来驱动更新(并设置 autoRequestPaint: false)。
[MEDIUM] 关闭仍在使用的 ElementImage
const source = new ElementImageSource({ resource: snapshot, autoClose: true });
const other = new ElementImageSource({ resource: snapshot }); // shares the snapshot
source.destroy(); // closes the snapshot — `other` is now a use-after-free
只有在 source 独占该快照时,才设置 autoClose: true。对于共享快照,请关闭 autoClose,并在最后一个 source 销毁后调用一次 snapshot.close()。
清理
source.destroy();
HTMLSource.destroy() 会分离 canvas 的 paint 监听器,并将其 canvas 引用置空。若设置了 autoClose,ElementImageSource.destroy() 会关闭快照;否则请自行调用 snapshot.close() 以释放内存。