前言

最近在了解webgl的一些东西, 突然看到一个第一人称的3d小游戏网站,游戏里可以锁定鼠标位置,使其不会移出浏览器,激发了我的学习欲望, 在我的知识储备里面不记得浏览器还有这个特性呀,今天来研究研究在浏览器里如何锁定鼠标位置

网站地址:

https://jblaha.art/sketchbook/0.4/ 一个第三人称的游乐场场景

去他Github看源码

image.png

经过不懈努力! 找到啦! Pointer Lock API,

Experimental: 这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

方法/属性

Pointer lock API, 和 Fullscreen API 类似,通过添加新方法来扩展 DOM 元素, requestPointerLock, 目前还是厂商前缀。按下面这样来写:

element.webkitRequestPointerLock(); // Chrome

element.mozRequestPointerLock(); // Firefox

// 兼容写法
element.requestPointerLock = element.requestPointerLock ||
                element.mozRequestPointerLock ||
                element.webkitRequestPointerLock;
            element.requestPointerLock();

目前MDN文档表示 requestPointerLock 的实现还是和 requestFullScreen 以及 Fullscreen API 紧紧地绑在一起的。一个元素在能够被指针锁定之前,必须首先进入全屏模式。(目前在chrome测试是不用进入全屏模式也可以锁定),锁定指针的过程是异步的,使用 (pointerlockchange, pointerlockerror) 事件来表明请求是成功还是失败了。这和 Fullscreen API 的工作方式是一致的,它使用 requestFullScreen方法,以及 fullscreenchangefullscreenerror 事件。

Pointer lock API 还扩展了 document 接口,添加了一个新的属性和一个新的方法。新的属性被用于访问当前被锁定的元素(如果有的话),并被命名为 pointerLockElement,目前也使用厂商前缀。 document 添加的新方法是 exitPointerLock ,顾名思义,它是用来退出指针锁定的。

pointerlockchange 事件

当指针锁定状态改变时 - 例如,当调用 requestPointerLock, exitPointerLock,用户按下 ESC 键,等等

该事件目前在 Firefox 中使用前缀的格式是 mozpointerlockchange ,在 Chrome 中是 webkitpointerlockchange

pointerlockerror 事件

当调用 requestPointerLockexitPointerLock而引发错误时, pointerlockerror 事件被分发到 document

对鼠标事件的扩展

partial interface MouseEvent {  
    readonly attribute long movementX;
    readonly attribute long movementY;
};  

movement 属性目前在 Firefox 中被加上前缀为 .mozMovementX 和 .mozMovementY , 在 Chrome 中为.webkitMovementX 和 .webkitMovementY。

鼠标事件的两个新参数 movementX 和 movementY 提供了鼠标位置的变化情况。这两个参数的值,等于两个MouseEvent 属性( screenX 和 screenY)之间值的变化程度,这些 MouseEvent 属性被存储在两个连续的鼠标移动事件( eNow 和 ePrevious)中。换言之,指针锁定参数 movementX = eNow.screenX - ePrevious.screenX。(注:不存在名为 eNow 或 ePrevious 的事件或属性,eNow 代指当前的鼠标移动事件,ePrevious 代指前一个鼠标移动事件)

demo

2021-10-27 05.24.05.gif

截屏看着是有鼠标的 实际上在浏览器上看鼠标是消失了的 上代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #pointer-lock-element{
            position: relative;
            width: 500px;
            height: 500px;
            background-color: #fff;
        }
        #movable-block{
            position: absolute;
            left: 0;
            top: 0;
            width: 50px;
            height: 50px;
            background-color: hotpink;
        }
    </style>
</head>
<body>
<button onclick="lockPointer();">锁定鼠标</button>
<div id="pointer-lock-element">
    <div id="movable-block"></div>
</div>
<script>
    // 注意: 截止本文撰写时, 仅有 Mozilla 和 WebKit 支持指针锁定。

    // 我们将要使之全屏并指针锁定的元素。
    let elem;
    let movableBlock = document.querySelector('#movable-block')

    document.addEventListener("mousemove", function (e) {
        var movementX = e.movementX ||
            e.mozMovementX ||
            e.webkitMovementX ||
            0,
            movementY = e.movementY ||
                e.mozMovementY ||
                e.webkitMovementY ||
                0;

        var left = parseInt(movableBlock.style.left || 0)
        var top = parseInt(movableBlock.style.top || 0)

        left += movementX
        top += movementY

        movableBlock.style.left = left + 'px'
        movableBlock.style.top = top + 'px'


        // 打印鼠标移动的增量值。
        console.log("movementX=" + movementX, "movementY=" + movementY);
    }, false);

    function fullscreenChange() {
        if (document.webkitFullscreenElement === elem ||
            document.mozFullscreenElement === elem ||
            document.mozFullScreenElement === elem) { // 较旧的 API 大写 'S'.
            // 元素进入全屏模式了,现在我们可以请求指针锁定。
            elem.requestPointerLock = elem.requestPointerLock ||
                elem.mozRequestPointerLock ||
                elem.webkitRequestPointerLock;
            elem.requestPointerLock();
        }
    }

    document.addEventListener('fullscreenchange', fullscreenChange, false);
    document.addEventListener('mozfullscreenchange', fullscreenChange, false);
    document.addEventListener('webkitfullscreenchange', fullscreenChange, false);

    function pointerLockChange() {
        if (document.mozPointerLockElement === elem ||
            document.webkitPointerLockElement === elem) {
            console.log("指针锁定成功了。");
        } else {
            console.log("指针锁定已丢失。");
        }
    }

    document.addEventListener('pointerlockchange', pointerLockChange, false);
    document.addEventListener('mozpointerlockchange', pointerLockChange, false);
    document.addEventListener('webkitpointerlockchange', pointerLockChange, false);

    function pointerLockError() {
        console.log("锁定指针时出错。");
    }

    document.addEventListener('pointerlockerror', pointerLockError, false);
    document.addEventListener('mozpointerlockerror', pointerLockError, false);
    document.addEventListener('webkitpointerlockerror', pointerLockError, false);

    function lockPointer() {
        elem = document.getElementById("pointer-lock-element");
        // 在全屏模式下 启用鼠标锁定
        elem.requestFullscreen = elem.requestFullscreen ||
            elem.mozRequestFullscreen ||
            elem.mozRequestFullScreen || // 较旧的 API 把 ‘S’ 大写
            elem.webkitRequestFullscreen;
        elem.requestFullscreen();

        // 直接启用鼠标锁定
        // 当前测试Chrome版本 97.0.4672.2(正式版本)canary (x86_64)

        // elem.requestPointerLock = elem.requestPointerLock ||
        //     elem.mozRequestPointerLock ||
        //     elem.webkitRequestPointerLock;
        // elem.requestPointerLock();

    }
</script>
</body>
</html>

目录