Используйте API универсального датчика, чтобы получить доступ к датчикам на устройстве, таким как акселерометры, гироскопы и магнитометры.
Сегодня данные датчиков используются во многих приложениях для конкретных платформ, чтобы обеспечить такие варианты использования, как захватывающие игры, отслеживание фитнеса, а также дополненная или виртуальная реальность. Разве не было бы здорово преодолеть разрыв между платформерными и веб-приложениями? Введите API универсального датчика для Интернета!
Что такое API универсального датчика?
API универсального датчика — это набор интерфейсов, которые предоставляют сенсорным устройствам доступ к веб-платформе. API состоит из базового интерфейса Sensor
и набора конкретных классов датчиков, построенных на его основе. Наличие базового интерфейса упрощает процесс реализации и спецификации конкретных классов датчиков. Например, взгляните на класс Gyroscope
. Он очень крошечный! Основная функциональность определяется базовым интерфейсом, а Gyroscope
просто расширяет его тремя атрибутами, представляющими угловую скорость.
Некоторые классы датчиков взаимодействуют с реальными аппаратными датчиками, такими как, например, классы акселерометра или гироскопа. Их называют датчиками низкого уровня. Другие датчики, называемые датчиками слияния , объединяют данные от нескольких датчиков низкого уровня, чтобы предоставить информацию, которую в противном случае пришлось бы вычислять сценарию. Например, датчик AbsoluteOrientation
предоставляет готовую к использованию матрицу вращения размером четыре на четыре на основе данных, полученных с акселерометра, гироскопа и магнитометра.
Вы можете подумать, что веб-платформа уже предоставляет данные датчиков, и вы абсолютно правы! Например, события DeviceMotion
и DeviceOrientation
предоставляют данные датчика движения. Так зачем же нам нужен новый API?
По сравнению с существующими интерфейсами Generic Sensor API предоставляет множество преимуществ:
- Generic Sensor API — это платформа датчиков, которую можно легко расширить за счет новых классов датчиков, и каждый из этих классов сохранит общий интерфейс. Клиентский код, написанный для одного типа датчиков, можно повторно использовать для другого с минимальными изменениями!
- Вы можете настроить датчик. Например, вы можете установить частоту дискретизации, подходящую для нужд вашего приложения.
- Вы можете определить, доступен ли датчик на платформе.
- Показания датчиков имеют высокоточные временные метки, что обеспечивает лучшую синхронизацию с другими действиями в вашем приложении.
- Модели данных датчиков и системы координат четко определены, что позволяет производителям браузеров реализовывать совместимые решения.
- Интерфейсы на основе Generic Sensor не привязаны к DOM (то есть они не являются ни
navigator
, ни объектамиwindow
), и это открывает будущие возможности для использования API внутри сервис-воркеров или реализации его в автономной среде выполнения JavaScript, например, во встроенных устройствах. - Аспекты безопасности и конфиденциальности являются главным приоритетом для API универсального датчика и обеспечивают гораздо лучшую безопасность по сравнению со старыми API датчиков. Имеется интеграция с Permissions API.
- Автоматическая синхронизация с координатами экрана доступна для
Accelerometer
,Gyroscope
,LinearAccelerationSensor
,AbsoluteOrientationSensor
,RelativeOrientationSensor
иMagnetometer
.
Доступные общие API датчиков
На момент написания статьи существует несколько датчиков, с которыми вы можете поэкспериментировать.
Датчики движения:
-
Accelerometer
-
Gyroscope
-
LinearAccelerationSensor
-
AbsoluteOrientationSensor
-
RelativeOrientationSensor
-
GravitySensor
Датчики окружающей среды:
-
AmbientLightSensor
(за флагом#enable-generic-sensor-extra-classes
в Chromium.) -
Magnetometer
(за флагом#enable-generic-sensor-extra-classes
в Chromium.)
Обнаружение функций
Обнаружение функций аппаратных API является сложной задачей, поскольку вам необходимо определить, поддерживает ли браузер рассматриваемый интерфейс, и имеет ли устройство соответствующий датчик. Проверить, поддерживает ли браузер интерфейс, несложно. (Замените Accelerometer
любым из других интерфейсов, упомянутых выше .)
if ('Accelerometer' in window) {
// The `Accelerometer` interface is supported by the browser.
// Does the device have an accelerometer, though?
}
Для действительно значимого результата обнаружения функции вам также необходимо попытаться подключиться к датчику. Этот пример иллюстрирует, как это сделать.
let accelerometer = null;
try {
accelerometer = new Accelerometer({ frequency: 10 });
accelerometer.onerror = (event) => {
// Handle runtime errors.
if (event.error.name === 'NotAllowedError') {
console.log('Permission to access sensor was denied.');
} else if (event.error.name === 'NotReadableError') {
console.log('Cannot connect to the sensor.');
}
};
accelerometer.onreading = (e) => {
console.log(e);
};
accelerometer.start();
} catch (error) {
// Handle construction errors.
if (error.name === 'SecurityError') {
console.log('Sensor construction was blocked by the Permissions Policy.');
} else if (error.name === 'ReferenceError') {
console.log('Sensor is not supported by the User Agent.');
} else {
throw error;
}
}
Полифилл
Для браузеров, которые не поддерживают API Generic Sensor, доступен полифилл . Полифилл позволяет загружать только реализации соответствующих датчиков.
// Import the objects you need.
import { Gyroscope, AbsoluteOrientationSensor } from './src/motion-sensors.js';
// And they're ready for use!
const gyroscope = new Gyroscope({ frequency: 15 });
const orientation = new AbsoluteOrientationSensor({ frequency: 60 });
Что это за датчики? Как я могу их использовать?
Датчики — это область, которая, возможно, нуждается в кратком представлении. Если вы знакомы с датчиками, вы можете сразу перейти к практическому разделу кодирования . В противном случае давайте подробно рассмотрим каждый поддерживаемый датчик.
Акселерометр и датчик линейного ускорения
Датчик Accelerometer
измеряет ускорение устройства, на котором установлен датчик, по трем осям (X, Y и Z). Этот датчик является инерционным датчиком. Это означает, что, когда устройство находится в линейном свободном падении, общее измеренное ускорение будет 0 м/с 2 , а когда устройство лежит ровно на столе, ускорение в направлении вверх (ось Z) будет быть равным силе тяжести Земли, т.е. g ≈ +9,8 м/с 2 , так как измеряется сила стола, толкающая устройство вверх. Если вы толкнете устройство вправо, ускорение по оси X будет положительным или отрицательным, если устройство ускоряется справа налево.
Акселерометры можно использовать для подсчета шагов, определения движения или простой ориентации устройства. Довольно часто измерения акселерометра объединяются с данными из других источников для создания термоядерных датчиков, например датчиков ориентации.
LinearAccelerationSensor
измеряет ускорение, приложенное к устройству, на котором размещен датчик, без учета влияния силы тяжести. Когда устройство находится в состоянии покоя, например, лежит на столе, датчик будет измерять ускорение ≈ 0 м/с 2 по трем осям.
Датчик силы тяжести
Пользователи уже могут вручную получать показания, близкие к показаниям датчика силы тяжести, вручную проверяя показания Accelerometer
и LinearAccelerometer
, но это может быть обременительно и зависеть от точности значений, предоставляемых этими датчиками. Такие платформы, как Android, могут предоставлять показания гравитации как часть операционной системы, которая должна быть дешевле с точки зрения вычислений, предоставлять более точные значения в зависимости от оборудования пользователя и быть проще в использовании с точки зрения эргономики API. GravitySensor
возвращает эффект ускорения по осям X, Y и Z устройства под действием силы тяжести.
Гироскоп
Датчик Gyroscope
измеряет угловую скорость в радианах в секунду вокруг локальных осей X, Y и Z устройства. Большинство потребительских устройств оснащены механическими ( MEMS ) гироскопами, которые представляют собой инерционные датчики, измеряющие скорость вращения на основе инерционной силы Кориолиса . МЭМС-гироскопы склонны к дрейфу, вызванному гравитационной чувствительностью датчика, которая деформирует внутреннюю механическую систему датчика. Гироскопы колеблются на относительно высоких частотах, например, 10 кГц, и поэтому могут потреблять больше энергии по сравнению с другими датчиками.
Датчики ориентации
AbsoluteOrientationSensor
— это термоядерный датчик, который измеряет вращение устройства относительно системы координат Земли, а RelativeOrientationSensor
предоставляет данные, представляющие вращение устройства, на котором размещены датчики движения, относительно стационарной опорной системы координат.
Все современные 3D-фреймворки JavaScript поддерживают кватернионы и матрицы вращения для представления вращения; однако, если вы используете WebGL напрямую, OrientationSensor
имеет как свойство quaternion
, так и метод populateMatrix()
. Вот несколько фрагментов:
let torusGeometry = new THREE.TorusGeometry(7, 1.6, 4, 3, 6.3);
let material = new THREE.MeshBasicMaterial({ color: 0x0071c5 });
let torus = new THREE.Mesh(torusGeometry, material);
scene.add(torus);
// Update mesh rotation using quaternion.
const sensorAbs = new AbsoluteOrientationSensor();
sensorAbs.onreading = () => torus.quaternion.fromArray(sensorAbs.quaternion);
sensorAbs.start();
// Update mesh rotation using rotation matrix.
const sensorRel = new RelativeOrientationSensor();
let rotationMatrix = new Float32Array(16);
sensor_rel.onreading = () => {
sensorRel.populateMatrix(rotationMatrix);
torus.matrix.fromArray(rotationMatrix);
};
sensorRel.start();
const mesh = new BABYLON.Mesh.CreateCylinder('mesh', 0.9, 0.3, 0.6, 9, 1, scene);
const sensorRel = new RelativeOrientationSensor({ frequency: 30 });
sensorRel.onreading = () => mesh.rotationQuaternion.FromArray(sensorRel.quaternion);
sensorRel.start();
// Initialize sensor and update model matrix when new reading is available.
let modMatrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const sensorAbs = new AbsoluteOrientationSensor({ frequency: 60 });
sensorAbs.onreading = () => sensorAbs.populateMatrix(modMatrix);
sensorAbs.start();
// Somewhere in rendering code, update vertex shader attribute for the model
gl.uniformMatrix4fv(modMatrixAttr, false, modMatrix);
Датчики ориентации позволяют использовать различные варианты использования, такие как захватывающие игры, дополненная и виртуальная реальность.
Для получения дополнительной информации о датчиках движения, расширенных вариантах использования и требованиях ознакомьтесь с пояснительным документом по датчикам движения .
Синхронизация с координатами экрана
По умолчанию показания пространственных датчиков разрешаются в локальной системе координат, привязанной к устройству и не учитывающей ориентацию экрана.
Однако во многих случаях использования, таких как игры или дополненная и виртуальная реальность, требуется, чтобы показания датчиков обрабатывались в системе координат, которая вместо этого привязана к ориентации экрана.
Раньше переназначение показаний датчиков на экранные координаты приходилось реализовывать на JavaScript. Такой подход неэффективен, а также существенно увеличивает сложность кода веб-приложения; веб-приложение должно отслеживать изменения ориентации экрана и выполнять преобразования координат для показаний датчиков, что не является тривиальной задачей для углов Эйлера или кватернионов.
API Generic Sensor предоставляет гораздо более простое и надежное решение! Локальная система координат настраивается для всех определенных классов пространственных датчиков: Accelerometer
, Gyroscope
, LinearAccelerationSensor
, AbsoluteOrientationSensor
, RelativeOrientationSensor
и Magnetometer
. Передавая опцию referenceFrame
конструктору объекта датчика, пользователь определяет, будут ли возвращаемые показания разрешаться в координатах устройства или экрана .
// Sensor readings are resolved in the Device coordinate system by default.
// Alternatively, could be RelativeOrientationSensor({referenceFrame: "device"}).
const sensorRelDevice = new RelativeOrientationSensor();
// Sensor readings are resolved in the Screen coordinate system. No manual remapping is required!
const sensorRelScreen = new RelativeOrientationSensor({ referenceFrame: 'screen' });
Давайте кодировать!
API универсального датчика очень прост и удобен в использовании! Интерфейс Sensor имеет методы start()
и stop()
для управления состоянием датчика и несколько обработчиков событий для получения уведомлений об активации датчика, ошибках и новых доступных показаниях. Конкретные классы датчиков обычно добавляют свои особые атрибуты чтения к базовому классу.
Среда разработки
Во время разработки вы сможете использовать датчики через localhost
. Если вы занимаетесь разработкой для мобильных устройств, настройте переадресацию портов на своем локальном сервере, и все готово!
Когда ваш код будет готов, разверните его на сервере, поддерживающем HTTPS. Страницы GitHub обслуживаются через HTTPS, что делает их отличным местом для обмена демонстрациями.
Вращение 3D-модели
В этом простом примере мы используем данные датчика абсолютной ориентации для изменения кватерниона вращения 3D-модели. model
представляет собой экземпляр класса Three.js Object3D
, который имеет свойство quaternion
. Следующий фрагмент кода из демонстрации телефона для ориентации иллюстрирует, как можно использовать датчик абсолютной ориентации для вращения 3D-модели.
function initSensor() {
sensor = new AbsoluteOrientationSensor({ frequency: 60 });
sensor.onreading = () => model.quaternion.fromArray(sensor.quaternion);
sensor.onerror = (event) => {
if (event.error.name == 'NotReadableError') {
console.log('Sensor is not available.');
}
};
sensor.start();
}
Ориентация устройства будет отражена в вращении 3D- model
внутри сцены WebGL.
Перфоратор
Следующий фрагмент кода извлечен из демо-версии перфорометра и иллюстрирует, как можно использовать датчик линейного ускорения для расчета максимальной скорости устройства в предположении, что оно первоначально лежит неподвижно.
this.maxSpeed = 0;
this.vx = 0;
this.ax = 0;
this.t = 0;
/* … */
this.accel.onreading = () => {
let dt = (this.accel.timestamp - this.t) * 0.001; // In seconds.
this.vx += ((this.accel.x + this.ax) / 2) * dt;
let speed = Math.abs(this.vx);
if (this.maxSpeed < speed) {
this.maxSpeed = speed;
}
this.t = this.accel.timestamp;
this.ax = this.accel.x;
};
Текущая скорость рассчитывается как приближение к интегралу функции ускорения.
Отладка и переопределение датчиков с помощью Chrome DevTools
В некоторых случаях вам не нужно физическое устройство для работы с API универсального датчика. Chrome DevTools отлично поддерживает имитацию ориентации устройства .
Конфиденциальность и безопасность
Показания датчиков — это конфиденциальные данные, которые могут подвергаться различным атакам со стороны вредоносных веб-страниц. Реализации API-интерфейсов Generic Sensor налагают некоторые ограничения для снижения возможных рисков безопасности и конфиденциальности. Эти ограничения необходимо учитывать разработчикам, которые собираются использовать API, поэтому кратко перечислим их.
Только HTTPS
Поскольку API Generic Sensor — это мощная функция, браузер разрешает использовать ее только в безопасном контексте. На практике это означает, что для использования API Generic Sensor вам потребуется доступ к вашей странице через HTTPS. Во время разработки вы можете сделать это через http://localhost , но для производства вам понадобится HTTPS на вашем сервере. Рекомендации и рекомендации см. в коллекции «Безопасно и надежно» .
Интеграция политики разрешений
Интеграция политики разрешений в API универсального датчика контролирует доступ к данным датчиков для кадра.
По умолчанию объекты Sensor
могут создаваться только внутри основного фрейма или подкадров одного и того же происхождения, что предотвращает несанкционированное чтение данных датчиков iframe из разных источников. Это поведение по умолчанию можно изменить, явно включив или отключив соответствующие функции, контролируемые политикой .
Фрагмент ниже иллюстрирует предоставление доступа к данным акселерометра iframe с перекрестным происхождением, а это означает, что теперь там можно создавать объекты Accelerometer
или LinearAccelerationSensor
.
<iframe src="https://third-party.com" allow="accelerometer" />
Выдачу показаний датчиков можно приостановить
Показания датчиков доступны только на видимой веб-странице, т. е. когда пользователь фактически взаимодействует с ней. Более того, данные датчиков не будут передаваться в родительский кадр, если фокус пользователя изменится на подкадр с перекрестным происхождением. Это предотвращает ввод данных пользователем из родительского фрейма.
Что дальше?
В ближайшем будущем будет реализован набор уже определенных классов датчиков, таких как датчик внешней освещенности или датчик приближения ; однако благодаря большой расширяемости структуры Generic Sensor мы можем ожидать появления еще большего количества новых классов, представляющих различные типы датчиков.
Еще одним важным направлением будущей работы является улучшение самого API Generic Sensor. Спецификация Generic Sensor в настоящее время является кандидатом в рекомендацию, а это значит, что еще есть время внести исправления и внедрить новые функциональные возможности, необходимые разработчикам.
Вы можете помочь!
Спецификации датчиков достигли уровня зрелости «Кандидатская рекомендация» , поэтому мы высоко ценим отзывы разработчиков веб-сайтов и браузеров. Сообщите нам, какие функции было бы полезно добавить или что-то вы хотели бы изменить в текущем API.
Пожалуйста, не стесняйтесь сообщать о проблемах со спецификациями, а также об ошибках в реализации Chrome.
Ресурсы
- Демо-проекты: https://intel.github.io/generic-sensor-demos/
- Спецификация API общего датчика: https://w3c.github.io/sensors/
- Проблемы со спецификациями: https://github.com/w3c/sensors/issues.
- Список рассылки рабочей группы W3C: public-device-apis@w3.org
- Статус функции Chrome: https://www.chromestatus.com/feature/5698781827825664.
- Ошибки реализации: http://crbug.com?q=comComponent:Blink>Sensor.
Благодарности
Эта статья была рецензирована Джо Медли и Кейси Баскс .