跳到主要内容

Overlay与HTML叠加

GL canvas 不参与 CSS 布局,也没有可访问树。输入框、工具条、协作光标必须用 DOM overlay,定位矩阵与绘制 / 命中同一套相机。不要在 GL 里自研文本排版和 IME。

1. 世界点 → overlay 像素

export function placeOverlay(
el: HTMLElement,
canvas: HTMLCanvasElement,
worldToCss: (x: number, y: number) => { x: number; y: number },
worldX: number,
worldY: number,
): void {
const p = worldToCss(worldX, worldY);
el.style.position = "absolute";
el.style.left = `${p.x}px`;
el.style.top = `${p.y}px`;
}

worldToCss = 世界 → NDC → CSS 盒,必须与 指针到 NDC 互逆。overlay 放在与 canvas 同一 position: relative 容器 里,坐标相对容器,不要再加 getBoundingClientRect().left(除非 overlay 挂在 body)。

缩放:世界物体跟着相机变;正在输入的 textarea 不要 transform: scale,否则系统候选框错位。只更新 left/top,字号用 CSS 像素。

2. 事件所有权

区域谁收指针
画布空白、轨道球、框选canvas,touch-action: none
叠加按钮、输入DOM;拖场景经过它们时 overlay pointer-events: none
编辑中的 IME 框始终可点,不要 capture 到 canvas

setPointerCapture 打在 canvas 上做轨道球。不要用 movementX 累加当相机增量——浏览器缩放、指针锁定下会漂。用前后两帧的 eventToNdc 差。

3. 和 2D / SVG 叠层

同一元素不能既 webgl22d。生产:

  • 两张 canvas 叠:底 GL 场景,顶 2D 画笔迹 / 量尺;命中按 z 决定落到哪张。
  • DOM 在上:热区、标签、表单。

不要 texImage2D(canvas2d) 每帧把整张 UI 烤进 GL,除非你在做必须进着色器的 HUD。见 与 2D 叠层

4. 失败形态

症状原因
标签漂在物体旁边overlay 用 CSS 像素,绘制用 drawing buffer 像素
拖到按钮上轨道球中断overlay 没在拖时 pointer-events: none
输入框候选框飞走textarea 跟了相机 scale
点标签点到了 GL 物体标签未设命中,事件落到下面 canvas

权威资料

核对日期:2026-08-26