能力中的 API
键盘锁定与指针锁定
发布于
Keyboard Lock 让页面捕获操作系统允许的物理按键,Pointer Lock 则隐藏光标并向一个元素提供相对指针移动;两者面向游戏、远程访问应用等需要集中输入的界面。
MDN 把 Keyboard.lock() 和返回 Promise 的 requestPointerLock() 都标为有限可用。
Keyboard Lock 还仅限安全上下文,因此界面需要分别检测两项能力,并保留不锁定输入时仍可用的路径。
支持情况
链接到“支持情况”一节MDN 把两项 API 都标为有限可用。Keyboard Lock 要求安全上下文和由 JavaScript 发起的全屏;Pointer Lock 的支持情况及返回 Promise 的请求形式因浏览器而异。
await navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]);navigator.keyboard.unlock();
await element.requestPointerLock();document.exitPointerLock();不传列表调用 keyboard.lock(),会请求操作系统允许的全部有效按键代码。后一次调用会替换先前的按键集合。Keyboard Lock 规范在文档处于全屏状态时分派锁定的按键。Pointer Lock 会让 clientX、clientY、screenX 和 screenY 保持不变,而 movementX 与 movementY 继续报告相对移动。
keyboard.lock(keyCodes) 接受可选的 KeyboardEvent.code 值序列。element.requestPointerLock(options) 接受可选的 unadjustedMovement 布尔值;该值为 true 时,Pointer Lock 规范请求不经过操作系统鼠标加速的移动数据。
Keyboard Lock 的 lock() 算法要求代码运行在当前活动的顶层浏览上下文中,但不检查瞬时用户激活。Chrome 文档要求先进入由 JavaScript 发起的全屏。在活动顶层浏览上下文之外调用时,Promise 以 InvalidStateError 拒绝;代码无效时以 InvalidAccessError 拒绝;后一个锁定请求取代尚未完成的请求时,以 AbortError 拒绝。
Pointer Lock 要求瞬时激活,但文档此前成功锁定指针并用 exitPointerLock() 释放时例外。沙盒 iframe 需要 allow-pointer-lock token;按照 Pointer Lock 算法,请求平台不支持的未调整移动时会以 NotSupportedError 拒绝。
以下示例等 Pointer Lock 确认成功、Keyboard Lock Promise 兑现后才报告控制已锁定。任一请求失败时,屏幕控件仍可使用。
锁定控制,不支持时保留屏幕控件
链接到“锁定控制,不支持时保留屏幕控件”一节第一次点击进入全屏,第二次点击请求 Pointer Lock。pointerlockchange 确认锁定后,页面才请求 Keyboard Lock。任一 API 缺失时,界面保留普通键盘和指针处理,并提示集中控制不可用。
<section id="control-surface" tabindex="0" aria-label="交互控制区域"> <p id="lock-status" role="status">可使用屏幕按钮,或点击此区域开始。</p> <output id="position">x: 0, y: 0</output> <div id="screen-controls" aria-label="屏幕控件"> <button type="button" data-dx="0" data-dy="-1">上</button> <button type="button" data-dx="-1" data-dy="0">左</button> <button type="button" data-dx="1" data-dy="0">右</button> <button type="button" data-dx="0" data-dy="1">下</button> </div></section>
<script type="module">const surface = document.querySelector("#control-surface");const status = document.querySelector("#lock-status");const position = document.querySelector("#position");const controls = document.querySelector("#screen-controls");let x = 0;let y = 0;
function move(dx, dy) { x += dx; y += dy; position.value = `x: ${x}, y: ${y}`;}
controls.addEventListener("click", (event) => { const button = event.target.closest("button[data-dx]"); if (!button) return; move(Number(button.dataset.dx), Number(button.dataset.dy));});
surface.addEventListener("click", async (event) => { if (event.target.closest("#screen-controls")) return; const canLockKeyboard = "keyboard" in navigator && "lock" in navigator.keyboard; const canLockPointer = "requestPointerLock" in surface; const canFullscreen = "requestFullscreen" in surface;
if (!canLockKeyboard || !canLockPointer || !canFullscreen) { status.textContent = "请使用屏幕控件。"; return; }
if (document.fullscreenElement !== surface) { try { await surface.requestFullscreen(); status.textContent = "请再次点击控制区域以锁定输入。"; } catch (error) { status.textContent = `未进入全屏:${error.name}`; } return; }
const request = surface.requestPointerLock(); request?.catch((error) => { status.textContent = `Pointer Lock 失败:${error.name}`; });});
document.addEventListener("pointerlockchange", async () => { if (document.pointerLockElement !== surface) { navigator.keyboard?.unlock?.(); status.textContent = "控制已解锁。"; return; }
try { await navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]); status.textContent = "控制已锁定。按 Escape 退出。"; } catch (error) { document.exitPointerLock(); status.textContent = `Keyboard Lock 失败:${error.name}`; }});
document.addEventListener("pointerlockerror", () => { status.textContent = "Pointer Lock 失败,请使用屏幕控件。";});document.addEventListener("mousemove", (event) => { if (document.pointerLockElement !== surface) return; move(event.movementX, event.movementY);});</script>事件处理器等 Pointer Lock 确认成功后才报告结果。Keyboard Lock 失败时会释放 Pointer Lock,Pointer Lock 结束时会释放 Keyboard Lock;缺少支持或请求失败时,已经定义的屏幕控件仍可使用。相对指针移动与按钮操作会更新同一位置输出。
保留屏幕控件
链接到“保留屏幕控件”一节示例从不隐藏 #screen-controls。按钮处理器和相对指针移动都调用 move(),因此锁定开始前或请求失败后仍可使用这些控件。
另请参阅
链接到“另请参阅”一节- 用户激活,了解 Pointer Lock 请求使用的激活状态
- Screen Capture API,用于在远程访问界面中共享显示内容
- Keyboard Lock(wicg.github.io)
- Pointer Lock 2.0(w3c.github.io)
规范
| 规范 | 状态 |
|---|---|
| Keyboard Lock | WICG 草案 |
| Pointer Lock 2.0 | W3C 草案 |