Camera-pivot drags aren't canceled by lostpointercapture events inside iframe

Repro steps

  1. Save framer.html and framed.html locally (inlined below); framer just has an iframe that loads framed, which runs the stock playground example code.
  2. Open framer.html in any Chromium browser.
  3. Start a drag inside the iframe to rotate the camera.
  4. Finish the drag outside the iframe and mouse up.
  5. Mouse over the iframe again.

Result: camera pivots as though you were still dragging with the mouse down.
Expect: camera pivoting to be canceled since the mouse button isn’t down anymore.

In this case the browser doesn’t send the mouseup event since it happens outside of the iframe, but it does send a lostpointercapture event when the mouse re-enters the iframe. Presumably each of the places that does a setPointerCapture needs to also listen for lostpointercapture. I’ve verified manually (but hackily, which is why this is a bug report and not a PR) that canceling the drag on lostpointercapture does fix the issue.

framer.html

<html>
    <head>
    </head>
    <body>
        <iframe width="50%" height="50%" src="framed.html">
        </iframe>
    </body>
</html>

framed.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>Babylon Template</title>
        <style>
            html, body {
                overflow: hidden;
                width: 100%;
                height: 100%;
                margin: 0;
                padding: 0;
            }

            #renderCanvas {
                width: 100%;
                height: 100%;
                touch-action: none;
            }
        </style>
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
        <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
    </head>
   <body>
    <canvas id="renderCanvas" touch-action="none"></canvas> <!-- touch-action="none" for best results from PEP -->
    <script>
        var canvas = document.getElementById("renderCanvas"); // Get the canvas element
        var engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine

        /******* Add the create scene function ******/
        var createScene = function () {
            // This creates a basic Babylon Scene object (non-mesh)
            var scene = new BABYLON.Scene(engine);

            // This creates and positions a free camera (non-mesh)
            var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);

            // This targets the camera to scene origin
            camera.setTarget(BABYLON.Vector3.Zero());

            // This attaches the camera to the canvas
            camera.attachControl(canvas, true);

            // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
            var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

            // Default intensity is 1. Let's dim the light a small amount
            light.intensity = 0.7;

            // Our built-in 'sphere' shape.
            var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);

            // Move the sphere upward 1/2 its height
            sphere.position.y = 1;

            // Our built-in 'ground' shape.
            var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 6, height: 6}, scene);

            return scene;

        };
        /******* End of the create scene function ******/

        var scene = createScene(); //Call the createScene function

        // Register a render loop to repeatedly render the scene
        engine.runRenderLoop(function () {
                scene.render();
        });

        // Watch for browser/canvas resize events
        window.addEventListener("resize", function () {
                engine.resize();
        });
    </script>
   </body>
</html>
1 Like

Pinging @Cedric

Hey @PolygonalSun is it related to your work on camera input?

This isn’t related to anything that I’ve done but is definitely a good scenario to account for in my upcoming work.

As for why the mouseup event isn’t being sent, it’s because the listener for it is on the canvas, rather than the window, so it is being sent but not somewhere that the script is listening.

In any case, @mattdmorgan, this is a great catch!

@mattdmorgan wanna try to submit a PR?

1 Like

What APIs do you need to call to cancel the camera dragging on the lostpointercapture event?

@PolygonalSun any ideas ?

When you say cancel camera dragging, do you mean dragging during an active PointerDragBehavior or just general camera interaction?

The issue is that if the BJS canvas is in an iframe and you initiate a drag using one of the built-in interactions with a mouse and release the mouse cursor outside of the iframe, the dragging continues in the pressed down state when the mouse re-enters the iframe. we need the BJS mouse interactions to stop when they lose pointer capture.