API блокировки указателя
API блокировки указателя (ранее называвшийся API блокировки мыши) предоставляет методы ввода, основанные на перемещении мыши во времени (т.е., дельтах), а не только на абсолютном положении курсора мыши в области просмотра. Он предоставляет доступ к исходному перемещению мыши, блокирует цель событий мыши до единственного элемента, устраняет ограничения на то, насколько далеко может пройти движение мыши в одном направлении и скрывает курсор. Он идеально подходит, например, для игр в 3D от первого лица.
Более того, API полезен для любых приложений, которым требуется значительный ввод с мыши для управления перемещениями, вращения объектов и изменения данных, например, позволяя пользователям управлять углом обзора, перемещая мышь без нажатия кнопок. Кнопки тогда освобождаются для других действий. Другие примеры включают приложения для просмотра карт или спутниковых снимков.
Блокировка указателя позволяет получать доступ к событиям мыши, даже когда курсор выходит за пределы браузера или экрана. Например, ваши пользователи могут продолжать вращать или манипулировать 3D-моделью, перемещая мышь без ограничений. Без блокировки указателя вращение или манипуляция останавливается в тот момент, когда указатель достигает края браузера или экрана. Игроки в игры теперь могут нажимать кнопки и водить курсором мыши вперёд-назад, не беспокоясь о выходе из игровой области и случайном нажатии другой программы, которая заберёт фокус мыши из игры.
Основные понятия
Блокировка указателя связана с захватом указателя. Захват указателя обеспечивает непрерывную передачу событий целевому элементу во время перетаскивания мыши, но он прекращается при отпускании кнопки мыши. Блокировка указателя отличается от захвата указателя следующим:
- Она сохраняется: блокировка указателя не отпускает мышь, пока не будет сделан явный вызов API или пользователь не выполнит определённый жест для отключения.
- Она не ограничена границами браузера или экрана.
- Она продолжает отправлять события независимо от состояния кнопки мыши.
- Она скрывает курсор.
Обзор методов/свойств
В этом разделе приводится краткое описание каждого свойства и метода, относящегося к спецификации блокировки указателя.
requestPointerLock()
API блокировки указателя, аналогично API полноэкранного режима, расширяет элементы DOM, добавляя новый метод, requestPointerLock(). Следующий пример запрашивает блокировку указателя для элемента <canvas>:
canvas.addEventListener("click", async () => {
await canvas.requestPointerLock();
});
Примечание: Если пользователь вышел из блокировки указателя с помощью стандартного жеста разблокировки, или блокировка указателя ранее не была введена для этого документа, документ должен получить событие, сгенерированное в результате жеста вовлечения, прежде чем requestPointerLock будет успешным. (из https://w3c.github.io/pointerlock/#extensions-to-the-element-interface)
Операционные системы по умолчанию включают ускорение мыши, что полезно, когда иногда нужно медленное точное движение (например, для работы с графическими пакетами), но также нужно перемещать большие расстояния с более быстрым движением мыши (например, при прокрутке и выборе нескольких файлов). Однако для некоторых игр от первого лица предпочтительны исходные данные ввода мыши для управления вращением камеры — где одинаковое расстояние движения, быстрое или медленное, приводит к одинаковому вращению. Это обеспечивает лучший игровой опыт и более высокую точность, по мнению профессиональных игроков.
Чтобы отключить ускорение мыши на уровне ОС и получить доступ к исходному вводу мыши, можно установить unadjustedMovement в true:
canvas.addEventListener("click", async () => {
await canvas.requestPointerLock({
unadjustedMovement: true,
});
});
Обработка версий requestPointerLock() с promise и без
Приведённый выше фрагмент кода по-прежнему будет работать в браузерах, которые не поддерживают версию с promise API requestPointerLock() или опцию unadjustedMovement — оператор await разрешён перед функцией, которая не возвращает promise, а объект опций просто будет проигнорирован в браузерах, которые не поддерживают эту функцию.
Однако это может быть запутанным и иметь другие потенциальные побочные эффекты (например, попытка использовать requestPointerLock().then() вызовет ошибку в браузерах, которые не поддерживают эту функцию), поэтому вы можете явно обработать это, используя код по следующим строкам:
function requestPointerLockWithUnadjustedMovement() {
const promise = myTargetElement.requestPointerLock({
unadjustedMovement: true,
});
if (!promise) {
console.log("disabling mouse acceleration is not supported");
return;
}
return promise
.then(() => console.log("pointer is locked"))
.catch((error) => {
if (error.name === "NotSupportedError") {
// Some platforms may not support unadjusted movement.
// You can request again a regular pointer lock.
return myTargetElement.requestPointerLock();
}
});
}
pointerLockElement и exitPointerLock()
API блокировки указателя также расширяет интерфейс Document, добавляя новое свойство и новый метод:
-
pointerLockElementиспользуется для доступа к текущему заблокированному элементу (если таковой имеется). -
exitPointerLock()используется для выхода из блокировки указателя.
Свойство pointerLockElement полезно для определения, заблокирован ли какой-либо элемент в данный момент (например, для выполнения проверки на логическое значение), а также для получения ссылки на заблокированный элемент, если таковой имеется.
Вот пример использования pointerLockElement:
if (document.pointerLockElement === canvas) {
console.log("The pointer lock status is now locked");
} else {
console.log("The pointer lock status is now unlocked");
}
Метод Document.exitPointerLock() используется для выхода из блокировки указателя и, как и requestPointerLock, работает асинхронно, используя события pointerlockchange и pointerlockerror, о которых вы подробнее узнаете ниже.
document.exitPointerLock();
Событие pointerlockchange
Когда состояние блокировки указателя меняется (например, при вызове requestPointerLock() или exitPointerLock(), нажатии пользователем клавиши ESC и т.д.), событие pointerlockchange отправляется в document. Это простое событие, не содержащее дополнительных данных.
document.addEventListener("pointerlockchange", lockChangeAlert, false);
function lockChangeAlert() {
if (document.pointerLockElement === canvas) {
console.log("The pointer lock status is now locked");
// Do something useful in response
} else {
console.log("The pointer lock status is now unlocked");
// Do something useful in response
}
}
Событие pointerlockerror
При возникновении ошибки при вызове requestPointerLock() или exitPointerLock(), событие pointerlockerror отправляется в document. Это простое событие, не содержащее дополнительных данных.
document.addEventListener("pointerlockerror", lockError, false);
function lockError(e) {
alert("Pointer lock failed");
}
Расширения событий мыши
API блокировки указателя расширяет стандартный интерфейс MouseEvent атрибутами перемещения. Два новых атрибута для событий мыши — movementX и movementY — предоставляют изменение позиций мыши. Значения параметров совпадают с разницей между значениями свойств MouseEvent, screenX и screenY, которые хранятся в двух последующих событиях mousemove, eNow и ePrevious. Другими словами, параметр блокировки указателя movementX = eNow.screenX - ePrevious.screenX.
Заблокированное состояние
При включении блокировки указателя стандартные свойства MouseEvent clientX, clientY, screenX и screenY сохраняются постоянными, как если бы мышь не двигалась. Свойства movementX и movementY продолжают обеспечивать изменение позиции мыши. Нет ограничений для значений movementX и movementY, если мышь непрерывно движется в одном направлении. Понятие курсора мыши не существует, и курсор не может выйти за окно или быть ограничен краем экрана.
Разблокированное состояние
Параметры movementX и movementY действительны независимо от состояния блокировки мыши и доступны даже при разблокировке для удобства.
Когда мышь разблокирована, системный курсор может покинуть и снова войти в окно браузера. В этом случае значения movementX и movementY могут быть установлены в ноль.
Простой пример пошагового руководства
Мы написали демонстрацию захвата указателя мыши (посмотреть исходный код), чтобы показать, как использовать её для настройки простой системы управления. В этой демонстрации используется JavaScript для отрисовки шара поверх элемента <canvas>. Когда вы нажмёте на холст, захват указателя мыши используется для удаления указателя мыши и позволяет вам перемещать шар напрямую с помощью мыши. Давайте посмотрим, как это работает.
Мы задаём начальные позиции x и y на холсте:
let x = 50; let y = 50;
Далее мы настраиваем обработчик событий для выполнения метода requestPointerLock() на холсте при нажатии на него, что инициирует захват указателя. Проверка document.pointerLockElement нужна для того, чтобы определить, существует ли уже активный захват указателя — мы не хотим постоянно вызывать requestPointerLock() на холсте каждый раз при нажатии внутри него, если у нас уже есть захват указателя.
canvas.addEventListener("click", async () => {
if (!document.pointerLockElement) {
await canvas.requestPointerLock({
unadjustedMovement: true,
});
}
});
Примечание: Приведенный выше фрагмент кода работает в браузерах, которые не поддерживают версию метода requestPointerLock() с использованием обещаний. См. Обработка версий requestPointerLock() с и без обещаний для объяснения.
Теперь для отдельного обработчика событий захвата указателя: pointerlockchange. Когда это происходит, мы вызываем функцию lockChangeAlert() для обработки изменения.
document.addEventListener("pointerlockchange", lockChangeAlert, false);
Эта функция проверяет свойство pointerLockElement для определения, является ли это наш холст. Если это так, она присоединяет обработчик событий для обработки перемещения мыши с помощью функции updatePosition(). В противном случае она удаляет обработчик событий.
function lockChangeAlert() {
if (document.pointerLockElement === 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);
}
}
Функция updatePosition() обновляет положение шара на холсте (x и y), а также включает операторы if () для проверки, не вышел ли шар за края холста. Если да, то шар перемещается на противоположный край. Также включена проверка, вызывался ли ранее метод requestAnimationFrame(), и, если да, то он вызывается снова по необходимости. Затем вызывается функция canvasDraw() для обновления сцены холста. Также настроен трекер для вывода значений X и Y на экран для справки.
const tracker = document.getElementById("tracker");
let 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(() => {
animation = null;
canvasDraw();
});
}
}
Функция canvasDraw() рисует шар в текущих координатах x и y.
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();
}
Ограничения IFrame
Захват указателя может захватывать только один <iframe> за раз. Если вы захватываете один <iframe>, вы не можете захватить другой и передать ему целевой объект; захват указателя выдаст ошибку. Чтобы избежать этого ограничения, сначала разблокируйте захваченный <iframe>, а затем захватывайте другой.
В то время как <iframe> работают по умолчанию, "песочнице" <iframe> блокируют захват указателя. Чтобы избежать этого ограничения, используйте <iframe sandbox="allow-pointer-lock">.
Спецификации
| Спецификация |
|---|
| Pointer Lock 2.0 |
Совместимость с браузерами
| Рабочие столы | Мобильные устройства | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox для Android | Opera Android | Safari на iOS | Samsung Internet | WebView Android | |
Pointer_Lock_API |
37С версии 92 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
13С версии 92 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
5014–50 |
24С версии 78 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
10.1 |
37С версии 92 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
5014–50 |
24С версии 65 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
Нет |
3.0С версии 16 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
37С версии 92 возвращает обещание вместоundefined. Поведение отражает предлагаемое изменение спецификации. |
options_unadjustedMovement_parameter |
88Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
88Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
Нет | 74Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
Нет | 88Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
Нет | 63Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
Нет | 15.0Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
88Поддерживается на macOS Catalina 10.15.1+, Windows и ChromeOS. Пока не поддерживается на Linux. |
| Рабочие столы | Мобильные устройства | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox для Android | Opera Android | Safari на iOS | Samsung Internet | WebView Android | |
Pointer_Lock_API |
3722 | 13 | 5014–50 | 2415 | 10.1 | 3725 | 5014–50 | 2414 | Нет | 3.01.5 | 374.4 |
api.Document.exitPointerLock
Таблицы BCD загружаются только в браузере
api.Element.requestPointerLock
Таблицы BCD загружаются только в браузере
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API