Skip to content

Pointer ​

Pointer 把 Engine Canvas 上的原生鼠标事件定位到 topmost RenderView,使用原生 Raycaster 命中 Object3D,再沿 Entity 路由同步派发。

最小用法 ​

ts
const off = entity.onPointer('click', (event) => {
  console.log(event.targetEntity, event.intersection?.point);
  event.stopPropagation();
});

// 不再需要时:
off();

注册 handler 的 Entity 会自动进入对应事件族的候选索引。无需另建可拾取组件或 Pointer manager。

候选策略 ​

entity.pointerEvents 支持继承的三态策略:

值语义
null继承祖先;最终缺省为 listeners
listeners有对应事件族 handler 时参与
auto即使没有 handler 也进入候选,可作为遮挡体
none当前子树从命中候选中排除
ts
entity.pointerEvents = 'auto';
entity.pointerPriority = 10;

winner 先比较有效 pointerPriority,再比较 raycast 距离。priority 同样从 leaf 向 World root 继承,缺省为 0。

候选 add/remove/reparent 时只维护 Set 并标脏;下一次真实 hit 才压缩嵌套 Object3D roots,避免对祖先和后代重复 raycast。

View 与 Camera ​

Pointer 对当前有效 View 反向扫描,选择覆盖坐标的最后一个 View,因此与渲染的视觉 topmost 规则一致。然后把 client 坐标转换成该 View 内的 NDC。

ts
const hit = engine.pointer.hitTest(event.clientX, event.clientY);

if (hit.view !== null && hit.targetEntity !== null) {
  console.log(hit.view.world, hit.pointer, hit.intersections);
}

manual hitTest 不受 pointer.enabled、running 或 suspend gate 限制,但仍要求 Pointer/Engine 存活。它读取 Camera 已存储的 projection/world matrices,不调用隐式 update。

支持的事件 ​

pointerdown pointermove pointerup pointercancel
pointerover pointerout pointerenter pointerleave
click dblclick contextmenu wheel

普通事件沿 leaf → World root 冒泡。pointerenter/pointerleave 是逐节点非冒泡边界事件。stopPropagation() 停止后续 Entity,stopImmediatePropagation() 还会跳过当前 Entity 的剩余 handler。

Event 保留:

  • targetEntity / 同步 handler 内可读的 currentEntity;
  • intersection / intersections;
  • View-local pointer;
  • 原生 nativeEvent 与 Engine 原生 raycaster;
  • view 与 hover 的 relatedEntity。

click 与 missed ​

click 只有 pointerdown/up 命中同一 Entity 与 View 后,后续原生 click 才会发布。dblclick 还要求连续两次 Engine click identity 一致。

ts
const offMissed = engine.pointer.onMissed((event) => {
  if (event.sourceType === 'click') {
    clearSelection();
  }
});

missed 只用于已定位到 View 但未命中 Entity 的 click 与 contextmenu。坐标没有落入任何 View 时不会通知。

自动输入与性能 ​

自动事件只在 Engine running、至少成功呈现过一个非零帧、Pointer enabled 且未 suspend 时接纳:

ts
engine.pointer.enabled = false;
engine.pointer.enabled = true;

const release = engine.pointer.suspend();
// 业务交互期间暂停自动 Pointer
release();

Pointer 每帧保留最新一个 pointermove,同时保证 down/up/cancel/click/dblclick 的离散顺序。wheel 只在存在 wheel demand 时安装非 passive listener。

Engine 不实现业务级拖拽 capture 或 native setPointerCapture 所有权。需要拖拽锁定、手势状态机或越界策略时由业务在 handler 中基于原生事件和 pointer.suspend() 处理。

Raycaster 配置 ​

ts
engine.pointer.raycaster.layers.enable(1);
engine.pointer.raycaster.params.Line!.threshold = 0.1;

Pointer 公开唯一原生 Raycaster identity,方便使用 Three.js 自带的 layers/params 能力,不重复包装。

注意事项 ​

  • Object3D 与 Camera matrices 由业务在 mutation 后显式更新;
  • handler 同步 fail-fast,保留事件对象异步读取 currentEntity 会抛错;
  • destroy、World leave 等负向事实会立即取消 route 后续节点;
  • UI overlay 默认应 pointer-events: none;需要交互的控件局部设为 pointer-events: auto;
  • Pointer 是鼠标事件系统,不承诺触摸手势抽象。

下一步 ​

基于 MIT 协议发布(内部使用)