pointer-events について
pointer-events は、DOM やサーフェスの入力を Three.js シーンへ転送し、オブジェクトがポインターイベントに応答できるようにするライブラリです。オブジェクト単位の対象判定やポインター種別のフィルターに加え、交差判定方法を指定した独自ポインターを扱えます。
主な機能
- 01HTML イベントをシーンへ転送
- 02サーフェスイベントをポータルへ転送
- 03オブジェクト属性による対象選別
- 04ポインター種別によるイベント選別
- 05ポインターと交差判定のカスタマイズ
活用シーン
- 01Three.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 オブジェクトでポインターリスナーを発火させます。また、ヒットするオブジェクトをフィルタリングしたり、ポインター自体の形状を定義したりできます。
概要
このライブラリは、次の 3 つの柱で構成されています。
- 入力を転送します。
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 } が返されます。レンダーループで renderer.render(scene, camera) を呼び出す前に、毎フレーム update() を呼び出してください。すると、オブジェクトは 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()
ヒット対象のフィルタリング
オブジェクトごとに、どのイベントを受け取るかを制御するプロパティが 2 つあります。pointerEvents はオブジェクトをターゲットにできるかどうかを選択し、pointerEventsType はポインターの種類に基づいてフィルタリングします。
pointerEvents は CSS の pointer-events プロパティに基づいており、none、listener、auto を指定できます。
object.pointerEvents = 'none'
none— オブジェクトが直接ターゲットになることはありません。auto— CSS プロパティと同様に、オブジェクトは常にイベントのターゲットになります。listener— 追加の値であり、デフォルトです。オブジェクトにリスナーが登録されている場合にのみ、ターゲットになります。
listener がデフォルトであるほうが、3D シーンでは auto(ウェブでのデフォルト)よりも合理的です。3D シーンには、たとえばパーティクルのような半透明のコンテンツがよく含まれますが、そうしたコンテンツがデフォルトでポインターイベントを受け取るべきではないためです。
各オブジェクトでは、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 属性はオブジェクトのクローン時に複製されないため、クローンに再適用してください。