Changing enableRotate after pointerDown

Making a 3D rotatable cube, I want a user experience where if they “flick” a layer (quickly pointerDown then pointerUp in a direction) it spins a1/4 turn, but if they hold for more than 500ms, instead of spinning the layer, I set enableRotate to true, and if they subsequently move the cursor before letting up, the whole cube’s orientation (the scene, actually) becomes draggable.

The problem is that it seems setting enableRotate=true on the controls object after pointerDown does not result in that pointer being able to drag the scene, and the user has to life their finger up and put it back down, ruining the simple UX I’m after.

So my question is: is there a way to effect what I want, or will I have to go with a different user interaction model?

Would this help? The magic is done in lines 73-81 (the threshold is set to 300ms):
https://codepen.io/boytchev/full/ExrYyXr

A slow drag will activate rotation with OrbitControls.
A flick will make the model jitter, but no rotation will happen.

2 Likes

Well that’s pretty similar to what I tried, but I failed where you succeeded. Looks like I just need to debug. Thanks for showing me it’s possible!

Just can’t figure it out. Here’s my onDownPointer handler:

	const handlePointerDown = (e: ThreeEvent<PointerEvent>) => {
			addPointer(downPointers, e)
			controls.current!.enableRotate = false
			setTimeout(() => {controls.current!.enableRotate = true}, 500)
	}

(addPointer is just a stack of current pointers & has no bearing on this)

The only other difference I see is that I’m developing in React + R3-Fiber, hence the .current property on controls, which is a ref object.

Maybe React’s render cycle is the problem here??

I don’t fully understand the question, but wanted to mention the .reset() method of OrbitControls… I’ve used it to prevent unwanted behavior when toggling the controls mid-gesture.

Alright, I think there is some interference from either NextJS or (more likely) React/R3-Fiber.
So I’ve put up a repo with my case stripped down to the bare minimum.

I’m logging enable/disable actions in the console. You can see the assignments are happening as expected, but the scene doesn’t actually become rotatable when the timeOut is called.

Try mouse-down, wait half a sec, then drag both on and outside of the cube. Once disabled by the downPointer event, the scene doesn’t become draggable again until a new down-pointer event is in play. This is unlike the experience that I get in @PavelBoytchev’s sandbox , though it’s really the exact same approach, just wrapped in all the react stuff.

Here is the url to the page src up in github:

Here’s the running app you can poke at.

@PavelBoytchev , @manthrax, @seanwasere

OK, I found a workaround, though I do not really understand why it works over the original attempt.

When setting enableRotate to false at the beginning of the handler, wrap it in its own setTimeout with a value of 0 for the time. That’s it:

const handlePointerDown = (e: ThreeEvent<PointerEvent>) => {
    setTimeout(() => {
        console.log('disable')
        controls.current!.enableRotate = false
    }, 0)
    setTimeout(() => {
        console.log('enable')
        controls.current!.enableRotate = true
    }, 500)
}
1 Like

Ship it! :smiley: