跳到主要内容

HTML 与 CSS

界面不必绘制在画布内。画布上的 HTML 与 CSS 可以使用浏览器文本渲染和布局、带输入和自动补全的表单、无障碍访问、CSS 动画及各种 Web 字体和表情。PlayCanvas 应用常将两者组合,例如 HTML 菜单和设置页搭配画布内 HUD。

何时使用 HTML​

HTML 适合菜单、设置、表单、聊天、输入、长文本,以及需要无障碍访问的内容。画布界面适合 HUD、场景或 XR 界面,以及必须与场景同帧绘制、包含在画布捕获中或在编辑器构建的内容。详见两种方式的比较。

将 HTML 叠加到画布​

将 HTML 放在与画布位置、尺寸相同的覆盖容器中:

// A full-window overlay, for a canvas that fills the window
const overlay = document.createElement('div');
overlay.className = 'overlay';
overlay.innerHTML = `
<div class="score">Score: <span id="score">0</span></div>
<button id="pause">Pause</button>
`;
document.body.appendChild(overlay);

const style = document.createElement('style');
style.textContent = `
.overlay { position: fixed; inset: 0; pointer-events: none; font-family: sans-serif; color: white; }
.overlay .score { position: absolute; top: 16px; left: 16px; font-size: 24px; }
.overlay button { position: absolute; top: 16px; right: 16px; pointer-events: auto; }
`;
document.head.appendChild(style);

document.getElementById('pause').addEventListener('click', () => {
app.timeScale = app.timeScale === 0 ? 1 : 0;
});

覆盖层使用 pointer-events: none,使空白处指针穿透到画布;控件使用 pointer-events: auto 接收输入。

隔离 HTML 与游戏输入​

鼠标和键盘设备监听整个页面,因此 HTML 按钮点击也可能进入游戏,被 app.mouse.wasPressed() 和下方画布界面接收。文本框输入也会传到 app.keyboard,导致玩家输入姓名时角色同时前进。应在覆盖层停止事件传播:

// Clicks, wheel turns and key presses on the overlay's controls don't reach the game
for (const type of ['mousedown', 'mouseup', 'wheel', 'keydown', 'keyup']) {
overlay.addEventListener(type, event => event.stopPropagation());
}

覆盖层空白处仍通过 pointer-events: none 直接将事件交给画布。

定位 HTML​

对齐三维点​

摄像机 worldToScreen() 返回以画布左上角为原点的 CSS 像素,正是 HTML 所需坐标:

const onCanvas = new pc.Vec3();
app.on('update', () => {
camera.camera.worldToScreen(character.getPosition(), onCanvas);
// Hide the tag when the point is behind the camera
tag.style.display = onCanvas.z > 0 ? '' : 'none';
tag.style.transform = `translate(${onCanvas.x}px, ${onCanvas.y}px) translate(-50%, -100%)`;
});

这里假设标签容器覆盖画布,标签初始位于左上角。使用画布元素实现同类效果详见角色上方标签。引擎 annotations 脚本通过此方式为模型创建带标签热点:

注释热点

对齐元素​

canvasCorners 给出元素相对画布左上角的 CSS 像素角点,顺序为左下、右下、右上、左上。可精确将 HTML 覆盖到画布元素上,例如将文本框放到相应视觉元素上方:

function placeOver(element, html) {
const [bottomLeft, , topRight] = element.canvasCorners;
const canvasRect = app.graphicsDevice.canvas.getBoundingClientRect();
html.style.position = 'fixed';
html.style.left = `${canvasRect.left + bottomLeft.x}px`;
html.style.top = `${canvasRect.top + topRight.y}px`;
html.style.width = `${topRight.x - bottomLeft.x}px`;
html.style.height = `${bottomLeft.y - topRight.y}px`;
}

画布或元素尺寸变化时应重新调用。canvasCorners 适用于屏幕空间元素。界面文本输入教程用此方式构建完整文本框。

性能​

浏览器独立于画布对 HTML 布局和绘制。每帧修改文字与样式会产生开销,尤其是宽高等触发布局的变化。跟随角色等每帧移动的 HTML 应只改 transform,避免重新布局。大面积半透明覆盖层也会增加移动设备合成成本。

无障碍访问​

画布界面只是像素,屏幕阅读器无法读取,键盘用户也不能通过 Tab 访问控件。正确使用 <button>、<input> 和 <label> 等 HTML 元素时,可获得默认无障碍支持。菜单、设置等需要无障碍访问的内容宜采用 HTML,并为每个控件提供可见焦点状态。

画布中的 HTML​

HTML-in-Canvas 是浏览器 API,按原文所述从 Chrome 157 起默认开启,可将 HTML 绘制到纹理用于世界空间面板等场景。其他浏览器尚不支持,需要保留回退方案。用法详见 HTML-in-Canvas:

HTML 纹理

用 PCUI 构建工具​

构建编辑器和工具时,可以使用 PlayCanvas 编辑器自身采用的 HTML 组件库 PCUI,提供面板、检查器、树视图和输入控件等,支持普通 JavaScript 或 React。

另请参阅​