Search code examples
javascriptcssunity-game-engineunity-webgl

JS - Prevent mouse from leaving screen


I need to prevent the cursor from going out of the window. I've read that it's not possible, however I've already done that with a WebGL export from a Unity project. You first had to click the canvas, then the browser would show you a notification saying that you should press 'escape' to exit and get your cursor back. Since a Unity WebGL canvas can do it, I assume it can be done without Unity?


Solution

  • Use the HTML5 Pointer Lock API

    https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API

    Not my code example below, all creds to

    https://github.com/mdn/dom-examples/tree/master/pointer-lock

    HTML

    <!DOCTYPE HTML>
    <html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <title>Pointer lock demo</title>
      <link type="text/css" rel="stylesheet" href="style.css">
    </head>
    <body>
      <div class="information">
        <h1>Pointer lock demo</h1>
    
        <p>This demo demonstrates usage of the pointer lock API. Click on the canvas area and your mouse will directly control the ball inside the canvas, not your mouse pointer. You can press escape to return to the standard expected state.</p>
      </div>
    
      <canvas width="640" height="360">
        Your browser does not support HTML5 canvas
      </canvas>
      <div id="tracker"></div>
    
      <script src="app.js"></script>
    </body>
    </html>
    

    JS

    // helper function
    const RADIUS = 20;
    
    function degToRad(degrees) {
      var result = Math.PI / 180 * degrees;
      return result;
    }
    
    // setup of the canvas
    
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');
    
    var x = 50;
    var y = 50;
    
    function canvasDraw() {
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = "#f00";
      ctx.beginPath();
      ctx.arc(x, y, RADIUS, 0, degToRad(360), true);
      ctx.fill();
    }
    canvasDraw();
    
    // pointer lock object forking for cross browser
    
    canvas.requestPointerLock = canvas.requestPointerLock ||
                                canvas.mozRequestPointerLock;
    
    document.exitPointerLock = document.exitPointerLock ||
                               document.mozExitPointerLock;
    
    canvas.onclick = function() {
      canvas.requestPointerLock();
    };
    
    // pointer lock event listeners
    
    // Hook pointer lock state change events for different browsers
    document.addEventListener('pointerlockchange', lockChangeAlert, false);
    document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
    
    function lockChangeAlert() {
      if (document.pointerLockElement === canvas ||
          document.mozPointerLockElement === canvas) {
        console.log('The pointer lock status is now locked');
        document.addEventListener("mousemove", updatePosition, false);
      } else {
        console.log('The pointer lock status is now unlocked');  
        document.removeEventListener("mousemove", updatePosition, false);
      }
    }
    
    var tracker = document.getElementById('tracker');
    
    var animation;
    function updatePosition(e) {
      x += e.movementX;
      y += e.movementY;
      if (x > canvas.width + RADIUS) {
        x = -RADIUS;
      }
      if (y > canvas.height + RADIUS) {
        y = -RADIUS;
      }  
      if (x < -RADIUS) {
        x = canvas.width + RADIUS;
      }
      if (y < -RADIUS) {
        y = canvas.height + RADIUS;
      }
      tracker.textContent = "X position: " + x + ", Y position: " + y;
    
      if (!animation) {
        animation = requestAnimationFrame(function() {
          animation = null;
          canvasDraw();
        });
      }
    }