fix pointercapture/stoppropagation #596

This commit is contained in:
Paul Henschel
2020-07-29 10:43:08 +02:00
parent aed6016940
commit 4c69252f2b
2 changed files with 71 additions and 27 deletions

View File

@@ -1,32 +1,67 @@
import React, { useState, useCallback, useRef } from 'react'
import { Canvas } from 'react-three-fiber'
import * as THREE from 'three'
import React, { Suspense } from 'react'
import { Canvas, useLoader } from 'react-three-fiber'
function M() {
const t = useLoader(
THREE.TextureLoader,
`https://raw.githubusercontent.com/flowers1225/threejs-earth/master/src/img/earth4.jpg`
)
function Ball() {
const [pos, setPos] = useState(new THREE.Vector3())
const isPressed = useRef(false)
const onPointerDown = useCallback((e) => {
isPressed.current = true
e.target.setPointerCapture(e.pointerId)
}, [])
const onPointerUp = useCallback((e) => {
isPressed.current = false
e.target.releasePointerCapture(e.pointerId)
}, [])
const onPointerMove = useCallback((e) => {
if (isPressed.current) {
setPos(e.unprojectedPoint.clone().setZ(0))
}
}, [])
return (
<>
<mesh position={[-1, 0, 0]}>
<boxBufferGeometry attach="geometry" />
<meshBasicMaterial attach="material" color="hotpink" />
</mesh>
<mesh position={[1, 0, 0]}>
<boxBufferGeometry attach="geometry" />
<meshBasicMaterial attach="material" map={t} />
</mesh>
</>
<mesh position={pos} onPointerMove={onPointerMove} onPointerDown={onPointerDown} onPointerUp={onPointerUp}>
<boxBufferGeometry attach="geometry" args={[10, 32, 32]} />
<meshBasicMaterial attach="material" color={0x000000} />
</mesh>
)
}
function Cube() {
const onPointerOver = useCallback((e) => {
e.stopPropagation()
}, [])
return (
<mesh onPointerOver={onPointerOver} position={new THREE.Vector3(0, 0, -20)}>
<boxBufferGeometry attach="geometry" args={[800, 400, 40]} />
<meshBasicMaterial attach="material" color={0xfcfc00} />
</mesh>
)
}
export default function App() {
return (
<Canvas colorManagement style={{ background: '#272730' }}>
<Suspense fallback={null}>
<M />
</Suspense>
</Canvas>
<>
<Canvas
gl={{ alpha: false, antialias: false, logarithmicDepthBuffer: true }}
camera={{ fov: 75, position: [0, 0, 70] }}
orthographic
onCreated={({ gl }) => {
gl.setClearColor('white')
gl.toneMapping = THREE.ACESFilmicToneMapping
gl.outputEncoding = THREE.sRGBEncoding
}}>
<ambientLight intensity={1.1} />
<pointLight position={[100, 100, 100]} intensity={2.2} />
<pointLight position={[-100, -100, -100]} intensity={5} color="red" />
<Ball />
<Cube />
</Canvas>
</>
)
}

View File

@@ -321,8 +321,9 @@ export const useCanvas = (props: UseCanvasProps): DomEventHandlers => {
// #16031: (https://github.com/mrdoob/three.js/issues/16031)
// Allow custom userland intersect sort order
if (raycaster && raycaster.filter && sharedState.current)
if (raycaster && raycaster.filter && sharedState.current) {
intersects = raycaster.filter(intersects, sharedState.current)
}
for (let intersect of intersects) {
let eventObject: THREE.Object3D | null = intersect.object
@@ -382,9 +383,10 @@ export const useCanvas = (props: UseCanvasProps): DomEventHandlers => {
state.current.captured = []
}
// Push hits to the array
if (state.current.captured)
if (state.current.captured) {
state.current.captured.push(hit)
// Call the original event now
}
// Call the original event now
;(event.target as any).setPointerCapture(id)
}
@@ -398,7 +400,14 @@ export const useCanvas = (props: UseCanvasProps): DomEventHandlers => {
ray: defaultRaycaster.ray,
camera: state.current.camera,
// Hijack stopPropagation, which just sets a flag
stopPropagation: () => (raycastEvent.stopped = localState.stopped = true),
stopPropagation: () => {
// https://github.com/react-spring/react-three-fiber/issues/596
// Events are not allowed to stop propagation if the pointer has been captured
const cap = state.current.captured
if (!cap || cap.find((h) => h.eventObject.id === hit.eventObject.id)) {
raycastEvent.stopped = localState.stopped = true
}
},
// Pointer-capture needs the hit, on which the user may call stopPropagation()
// This makes it harder to use the actual event, because then we loose the connection
// to the actual hit, which would mean it's picking up all intersects ...
@@ -411,7 +420,7 @@ export const useCanvas = (props: UseCanvasProps): DomEventHandlers => {
// Event bubbling may me interrupted by stopPropagation, but that should only include
// events that aren't capturing, since these are in the middle of a gesture and should not
// be disturbed until they resolve.
if (localState.stopped === true && localState.captured == false) {
if (localState.stopped === true) {
// Propagation is stopped, remove all other hover records
// An event handler is only allowed to flush other handlers if it is hovered itself
if (hovered.size && Array.from(hovered.values()).find((i) => i.object === hit.object)) {