Cursor locks to rotating the camera on mouse up outside of iframe

Hello,

I’m using an ArcRotateCamera and my whole scene webpage is in an iframe. When I drag to rotate and leave the iframe with the cursor so the mouse up event fires while being outside, babylon thinks it is still rotating when re-entering the iframe. This is rather annoying, because when you click to drag again you don’t rotate, you zoom and pan enormously. This also happens when the mouse up event fires on any overlayed HTML element.
I basically need to detect the mouse up event anywhere in the browser and then tell babylon it’s time to stop rotating. Any ideas or other suggestions?

Interestingly enough this only happened when the path for the source attribute of the iframe was relative. When I tried an absolute version of the same webpage the camera wasn’t locked in rotation mode.

pinging @PolygonalSun for some direction/advice

1 Like

@Regit, by any chance, would you be able to provide any kind of example or code snippet so that I can attempt to repro? I’m gonna try to create an example in the meantime so that I can see if I can see what you’re seeing.

1 Like

I extracted the issue from my project and uploaded it for you to look at here: https://regitgit.github.io/babylonjs-iframe/
Click on the image to open the iframe. Then drag to rotate and move anywhere out of the iframe. When you re-enter the rotating is locked.

This would also happen when you released on the ‘X’-exit button. But I’m currently toggling its pointer events on pointerdown/up. However this only gets checked inside the contentWindow of the iframe, maybe there is a way to check if the click event started on the contentWindow but ended outside of it?

Small update: I’m getting the pointer up event outside of the iframe simply by this:
document.addEventListener('pointerup', function(event) { }

I read about a similar issue in this thread: https://forum.babylonjs.com/t/arccamera-iframe-mouseup-not-triggered/9251
But scene.simulatePointerUp() doesn’t work… Looks like scene.pick(1000, 500) doesn’t work as a parameter. The simulated event then still shows x: 0 y: 0. But even then it should still stop the rotation?

Another approach was using:

var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ("pointerup", true, true);
canvas.dispatchEvent (clickEvent);

Here the event somewhat works. Looks like its fired at (0, 0), since the model rotates towards there. And you guessed it, the rotation is still locked, that’s probably why it rotates. But in the end the pointer up event doesn’t seem to cancel the rotation lock :confused: .

I want to shift the attention to this. I tested it in another project, same thing. No rotation lock in babylon scenes coming from an absolute path somewhere on the web.
This has to be a bug right?

@PolygonalSun will have a look shortly

Hey @Regit, thanks for the demo! It looks like the pointerup event isn’t being handled. I won’t be able to dig into it much more today but I’ll try to take a closer look at it either tomorrow or Monday.

2 Likes

Hey, is there any news on this?

You can actually scratch the idea of it having something to do with absolute/relative path. That was supposedly just the case in localhost, but makes no difference on actual web.

@PolygonalSun is in vacation for another 2 weeks, yup this is indeed this period of the year :slight_smile: I bet he ll be looking into ASAP when he is back. If the fix is urgent, please let me know ?

Only 2 weeks? Give this man more off-time! :grinning_face_with_smiling_eyes:
Not too urgent, take your time.

2 Likes

@Regit I love your answer !!!

1 Like

Hello @PolygonalSun, are you still on this? Otherwise I’ll try to implement some workarounds…

Hey @Regit, I’m still on this. I’m currently trying to figure out why a specific event isn’t firing or at least being picked up properly.

1 Like

For anyone interested: The workaround I’m using right now is PointerLock. Works pretty well and needs minimal setup. It also allows infinite rotation instead of the normal rotation that’s restricted by the canvas’ bounds. Maybe that’s a reason to keep it like this even if the issue is fixed.

You just have to initialize it and add it to pointerdown/pointerup. I do it right in the beginning after getting the canvas:

var canvas = document.getElementById("renderCanvas");

canvas.requestPointerLock = canvas.requestPointerLock ||
                    canvas.mozRequestPointerLock;

document.exitPointerLock = document.exitPointerLock ||
                        document.mozExitPointerLock;

canvas.onpointerdown = function() {
    canvas.requestPointerLock();
}
canvas.onpointerup = function() {
    document.exitPointerLock();
}

The only downside was that I couldn’t use dblclick on the canvas anymore because of the locking. But with minimal additional code you can “implement” your own dblclick.

Edit: Forgot to mention that now I do it like this there’s an exception being thrown:

Uncaught DOMException: Failed to execute 'setPointerCapture' on 'Element': InvalidStateError
    at HTMLCanvasElement._pointerDownEvent (https://preview.babylonjs.com/babylon.js:16:3780802)

It isn’t interfering with anything it seems. Any idea how to catch it nontheless?

For our code, we add set a pointer capture so that the event data can be grabbed, event when the cursor leaves the element area. Because we set this as part of our standard input, it’s trying to setPointerCapture when there’s already a pointer lock active (a pointer capture can’t be set when a pointer lock is active, by design). That’s why you’re seeing that DOMException.

The odd thing is, the pointer capture SHOULD be capturing your event data, even when you leave the iframe and that’s what’s got me confused.

So I’ve been looking at this issue and it seems like there is a fundamental issue with iframes and not properly firing pointerout or pointerleave events, which would be needed to “let go” of the mouse button. As far as using pointerlock as a workaround, I created a PR to address the error that comes up so you should be able to use pointerlock without issue: DeviceInputSystem: Catch pointer capture calls during pointer lock by PolygonalSun · Pull Request #11120 · BabylonJS/Babylon.js (github.com)

2 Likes

Thanks for your efforts. Will there be more shots at this issue or should I begin to live with the pointer lock setup? :sweat_smile: