关于 pointer-events
pointer-events 是将 DOM 或物体表面输入转发到 Three.js 场景的库,供场景对象响应指针事件。它支持按对象和指针类型筛选命中,并允许自定义指针及交点计算方式。
主要功能
- 01转发 HTML 事件到场景
- 02转发表面事件到门户场景
- 03按对象属性筛选事件目标
- 04按指针类型过滤事件
- 05自定义指针与相交计算
适用场景
- 01让 Three.js 对象响应页面输入
- 02创建可交互的门户场景
- 03限制透明物体捕获指针事件
- 04通过射线或球体实现自定义交互
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ drawcall-ai/ skills --skill pointer-events安装后在 Claude Code 中输入/pointer-events确认已加载。
详细介绍
通过 @pmndrs/pointer-events 使用指针事件
@pmndrs/pointer-events 会将真实输入转发到 Three.js 场景中,使 3D 对象触发指针监听器、筛选会命中的对象,并允许你定义指针本身的形状。
概述
该库基于三大支柱:
- 转发输入。
forwardHtmlEvents将 DOM 事件引入场景;forwardObjectEvents将来自某个表面的事件引入单独的门户场景。两者都会返回一个每帧运行的update()。 - 筛选命中对象。
pointerEvents属性(源自 CSS:none/listener/auto)和pointerEventsType属性(all/allow/deny/函数)会按对象决定哪些事件会命中。 - 定义指针。 你可以构建自己的
Pointer,并选择如何计算其交点。
这些功能可以串联使用:转发会生成各自带有 pointerType 的事件;筛选会按对象决定这些事件中哪些会命中;自定义指针则决定如何以及在哪里计算交点。下文将按此顺序介绍:将事件转发到场景、筛选命中对象,以及自定义指针。
将事件转发到场景
转发功能会在场景中安装指针事件源,并提供一个 update(),供你在每帧推进事件。
forwardHtmlEvents 会将 HTML 文档的事件转发到 3D 场景中。调用时传入 DOM 目标、相机获取函数和场景 — forwardHtmlEvents(document.body, () => camera, scene) — 它会返回 { update }。在渲染循环中,每帧调用一次 update(),并且要在 renderer.render(scene, camera) 之前调用。随后,对象便可通过 addEventListener('pointerover' | 'pointerout', (e: PointerEvent) => ...) 接收事件;PointerEvent 类型与 forwardHtmlEvents 一同从 @pmndrs/pointer-events 导入。每个事件都包含 e.point(一个向量,可调用 e.point.toArray()),并且你可以为同一种事件类型附加多个监听器。
import * as THREE from 'three'
import { forwardHtmlEvents, PointerEvent } from '@pmndrs/pointer-events'
const width = window.innerWidth,
height = window.innerHeight
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 10)
camera.position.z = 1
const { update } = forwardHtmlEvents(document.body, () => camera, scene)
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color('red') })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
mesh.addEventListener('pointerover', (e: PointerEvent) => material.color.set('blue'))
mesh.addEventListener('pointerover', (e: PointerEvent) => console.log(e.point.toArray()))
mesh.addEventListener('pointerout', (e: PointerEvent) => material.color.set('red'))
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
renderer.setAnimationLoop(() => {
update()
renderer.render(scene, camera)
})
forwardObjectEvents 会将来自某个表面(例如一个平面)的事件转发到单独的场景中,并将该场景渲染到该表面上,从而生成一个可交互的门户。它的用法与 forwardHtmlEvents 相同:传入表面网格、相机获取函数和门户场景。它会返回自己的 update,此处命名为 updatePortal;每帧都必须与主 update() 一同调用。
const { update: updatePortal } = forwardObjectEvents(planeMesh, () => portalCamera, portalScene)
// in the render loop, alongside the main update():
updatePortal()
筛选命中对象
两个逐对象属性控制哪些事件会到达对象:pointerEvents 决定对象是否可作为目标,pointerEventsType 则按指针类型筛选。
pointerEvents 基于 CSS 的 pointer-events 属性,取值为 none、listener 或 auto。
object.pointerEvents = 'none'
none— 对象永远不会被直接指定为目标。auto— 对象始终会成为事件的目标,与 CSS 属性行为一致。listener— 额外提供的取值,也是默认值:只有对象附加了监听器时,它才会成为目标。
对于 3D 场景,listener 默认值比 auto(Web 默认值)更合理,因为场景中通常包含半透明内容,例如粒子;这类内容默认不应拦截指针事件。
每个对象还可以通过 pointerEventsType 按指针类型筛选,默认值为 all(接受来自所有指针类型的事件)。若要筛选,可设置 { allow: "screen-mouse" } 或 { deny: "screen-touch" }(screen-mouse 表示通过 2D 屏幕操作的普通鼠标)。allow 和 deny 都接受字符串或字符串数组;pointerEventsType 也接受函数,以实现自定义逻辑。
默认使用的指针类型包括 screen-touch、screen-pen、ray、grab 和 touch。从 DOM 转发的事件所带的 pointerType 以 screen- 为前缀;通过门户经 forwardObjectEvents 转发的事件所带的 pointerType 则以 forward- 为前缀。
自定义指针
你可以创建自己的 Pointer——例如,将指针放在相机内部或其他位置,让第一人称控制器能够与环境交互。自定义 Pointer 可以使用普通的 Ray 进行相交检测,也可以使用一组 Lines,或使用 Sphere 处理抓取和触摸事件。
注意事项
克隆对象时,不会复制 Mesh/Object3D 的 pointerEvents 属性;请在克隆对象上重新应用该属性。