Usa la API de Generic Sensor para acceder a los sensores del dispositivo, como acelerómetros, giroscopios y magnetómetros.
Hoy en día, los datos de sensores se usan en muchas aplicaciones específicas de la plataforma para habilitar casos de uso como videojuegos envolventes, seguimiento de la actividad física y realidad aumentada o virtual. ¿No sería genial cerrar la brecha entre las aplicaciones web y las específicas? Ingresa la API de Generic Sensor para la Web.
¿Qué es la API de Generic Sensor?
La API de Generic Sensor es un conjunto de interfaces que exponen los dispositivos con sensores a la plataforma web. La API consta de la interfaz base de Sensor
y un conjunto de clases concretas de sensores construidas en la parte superior. Tener una interfaz base simplifica el proceso de implementación y especificación de las clases concretas de sensores. Por ejemplo, observa la clase Gyroscope
. ¡Es superpequeño! La interfaz base especifica la funcionalidad principal y Gyroscope
simplemente la extiende con tres atributos que representan la velocidad angular.
Algunas clases de sensores interactúan con sensores de hardware reales, como las clases de acelerómetro o giroscopio. Estos se denominan sensores de bajo nivel. Otros sensores, conocidos como sensores de fusión, combinan datos de varios sensores de bajo nivel para exponer información que una secuencia de comandos, de otro modo, necesitaría calcular. Por ejemplo, el sensor AbsoluteOrientation
proporciona una matriz de rotación de cuatro por cuatro lista para usar según los datos obtenidos del acelerómetro, el giroscopio y el magnetómetro.
Podrías pensar que la plataforma web ya proporciona datos de sensores y tienes toda la razón. Por ejemplo, los eventos DeviceMotion
y DeviceOrientation
exponen los datos del sensor de movimiento. ¿Por qué necesitamos una API nueva?
En comparación con las interfaces existentes, la API de Generic Sensor proporciona una gran cantidad de ventajas:
- La API de Generic Sensor es un framework de sensor que se puede extender fácilmente con nuevas clases de sensores, y cada una de estas clases mantendrá la interfaz genérica. El código de cliente escrito para un tipo de sensor puede reutilizarse en otro, con muy pocas modificaciones.
- Puedes configurar el sensor. Por ejemplo, puedes configurar la frecuencia de muestreo adecuada para las necesidades de tu aplicación.
- Puedes detectar si hay un sensor disponible en la plataforma.
- Las lecturas del sensor tienen marcas de tiempo de alta precisión, lo que permite una mejor sincronización con otras actividades de tu aplicación.
- Los modelos de datos de sensores y los sistemas de coordenadas están claramente definidos, lo que permite a los proveedores de navegadores implementar soluciones interoperables.
- Las interfaces genéricas basadas en el sensor no están vinculadas al DOM (lo que significa que no son objetos
navigator
niwindow
), y esto abre oportunidades futuras para usar la API en service worker o implementarla en entornos de ejecución de JavaScript sin interfaz gráfica, como dispositivos incorporados. - Los aspectos de seguridad y privacidad son la prioridad principal para la API de Generic Sensor y proporcionan mucha mejor seguridad en comparación con las APIs de sensores más antiguas. Hay integración con la API de Permissions.
- La sincronización automática con coordenadas de pantalla está disponible para
Accelerometer
,Gyroscope
,LinearAccelerationSensor
,AbsoluteOrientationSensor
,RelativeOrientationSensor
yMagnetometer
.
APIs de sensores genéricas disponibles
Al momento de escribir, existen varios sensores con los que puedes experimentar.
Sensores de movimiento:
Accelerometer
Gyroscope
LinearAccelerationSensor
AbsoluteOrientationSensor
RelativeOrientationSensor
GravitySensor
Sensores ambientales:
AmbientLightSensor
(detrás de la marca#enable-generic-sensor-extra-classes
en Chromium)Magnetometer
(detrás de la marca#enable-generic-sensor-extra-classes
en Chromium)
Detección de funciones
La detección de funciones de las APIs de hardware es complicada, ya que debes detectar si el navegador es compatible con la interfaz en cuestión y si el dispositivo tiene el sensor correspondiente. Comprobar si el navegador admite una interfaz es sencillo. (Reemplaza Accelerometer
por cualquiera de las otras interfaces mencionadas anteriormente).
if ('Accelerometer' in window) {
// The `Accelerometer` interface is supported by the browser.
// Does the device have an accelerometer, though?
}
Para obtener un resultado realmente significativo en la detección de funciones, también debes intentar conectarte al sensor. Este ejemplo ilustra cómo hacerlo.
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;
}
}
Polyfill
Para los navegadores que no son compatibles con la API Generic Sensor, hay un polyfill disponible. El polyfill te permite cargar solo las implementaciones de los sensores relevantes.
// 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 });
¿Qué son todos estos sensores? ¿Cómo puedo usarlas?
Los sensores son un área que podría necesitar una breve introducción. Si conoces los sensores, puedes pasar directamente a la sección de programación práctica. De lo contrario, veamos en detalle cada sensor compatible.
Acelerómetro y sensor de aceleración lineal
El sensor Accelerometer
mide la aceleración de un dispositivo que aloja el sensor en tres ejes (X, Y y Z). Este sensor es un sensor inercial, lo que significa que, cuando el dispositivo está en caída libre lineal, la aceleración total medida sería de 0 m/s2 y, cuando un dispositivo se encuentra en una posición plana sobre una mesa, la aceleración hacia arriba (eje Z) será igual a la gravedad de la Tierra, es decir, la medición de g Dynamic +9.8 m/s hacia arriba. Si presionas el dispositivo hacia la derecha, la aceleración en el eje X sería positiva o negativa si se acelera el dispositivo desde la derecha hacia la izquierda.
Los acelerómetros se pueden usar para tareas como el recuento de pasos, la detección de movimiento o la orientación simple del dispositivo. Muy a menudo, las mediciones del acelerómetro se combinan con datos de otras fuentes para crear sensores de fusión, como sensores de orientación.
El objeto LinearAccelerationSensor
mide la aceleración que se aplica al dispositivo que aloja el sensor, sin incluir la contribución de la gravedad. Cuando un dispositivo está en reposo (por ejemplo, acostado sobre una mesa), el sensor mediría una aceleración targetSdkVersion 0 m/s2 en tres ejes.
Sensor de gravedad
Los usuarios ya pueden obtener de forma manual lecturas cercanas a las de un sensor de gravedad con la inspección manual de las lecturas de Accelerometer
y LinearAccelerometer
, pero esto puede ser engorroso y depende de la exactitud de los valores que proporcionan esos sensores. Las plataformas como Android pueden proporcionar lecturas de gravedad como parte del sistema operativo, lo que debería ser más económico en términos de procesamiento, proporcionar valores más precisos según el hardware del usuario y ser más fáciles de usar en términos de ergonomía de la API. El objeto GravitySensor
muestra el efecto de la aceleración en los ejes X, Y y Z del dispositivo debido a la gravedad.
Giroscopio
El sensor Gyroscope
mide la velocidad angular en radianes por segundo alrededor de los ejes X, Y y Z locales del dispositivo. La mayoría de los dispositivos de consumidores tienen giroscopios mecánicos (MEMS), que son sensores inerciales que miden la velocidad de rotación en función de la fuerza inercial de Coriolis. Los giroscopios MEMS son propensos a la desviación causada por la sensibilidad gravitacional del sensor, que deforma el sistema mecánico interno del sensor. Los giroscopios oscilan a frecuencias relativamente altas, p.ej., 10 s de kHz y, por lo tanto, pueden consumir más energía en comparación con otros sensores.
Sensores de orientación
El AbsoluteOrientationSensor
es un sensor de fusión que mide la rotación de un dispositivo en relación con el sistema de coordenadas de la Tierra, mientras que RelativeOrientationSensor
proporciona datos que representan la rotación de un dispositivo que aloja sensores de movimiento en relación con un sistema de coordenadas de referencia estacionaria.
Todos los frameworks modernos de JavaScript en 3D admiten cuaterniones y matrices de rotación para representar la rotación. Sin embargo, si usas WebGL directamente, OrientationSensor
tiene una propiedad quaternion
y un método populateMatrix()
de manera conveniente.
A continuación, se muestran algunos fragmentos:
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);
Los sensores de orientación permiten varios casos de uso, como videojuegos envolventes, realidad aumentada y virtual.
Para obtener más información sobre los sensores de movimiento, los casos de uso avanzados y los requisitos, consulta el documento de explicación sobre los sensores de movimiento.
Sincronización con coordenadas de pantalla
De forma predeterminada, las lecturas de los sensores espaciales se resuelven en un sistema de coordenadas local que está vinculado al dispositivo y no tiene en cuenta la orientación de la pantalla.
Sin embargo, muchos casos de uso, como juegos o realidad aumentada y virtual, requieren que las lecturas de los sensores se resuelvan en un sistema de coordenadas que, en su lugar, esté vinculado a la orientación de la pantalla.
Anteriormente, la reasignación de las lecturas de los sensores a las coordenadas de la pantalla tenía que implementarse en JavaScript. Este enfoque es ineficiente y también aumenta significativamente la complejidad del código de la aplicación web; esta debe observar los cambios de orientación de la pantalla y realizar transformaciones de coordenadas para las lecturas de los sensores, lo que no es algo trivial para los ángulos o cuaterniones de Euler.
La API de Generic Sensor proporciona una solución mucho más simple y confiable. El sistema de coordenadas local se puede configurar para todas las clases de sensores espaciales definidas: Accelerometer
, Gyroscope
, LinearAccelerationSensor
, AbsoluteOrientationSensor
, RelativeOrientationSensor
y Magnetometer
. Cuando se pasa la opción referenceFrame
al constructor del objeto del sensor, el usuario define si las lecturas mostradas se resolverán en coordenadas de dispositivo o pantalla.
// 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' });
¡A programar!
La API de Generic Sensor es muy simple y fácil de usar. La interfaz del Sensor tiene los métodos start()
y stop()
para controlar el estado del sensor y varios controladores de eventos para recibir notificaciones sobre la activación del sensor, los errores y las lecturas recientemente disponibles. Por lo general, las clases concretas de sensores agregan sus atributos de lectura específicos a la clase base.
Entorno de desarrollo
Durante el desarrollo, podrás usar sensores a través de localhost
. Si desarrollas contenido para dispositivos móviles, configura la redirección de puertos en tu servidor local. ¡Ya estás listo!
Cuando tu código esté listo, impleméntalo en un servidor que admita HTTPS. Las páginas de GitHub se entregan a través de HTTPS, por lo que es un excelente lugar para compartir tus demostraciones.
Rotación de modelos 3D
En este ejemplo sencillo, usamos los datos de un sensor de orientación absoluta para modificar el cuaternión de rotación de un modelo 3D. El model
es una instancia de clase Object3D
de tres.js que tiene una propiedad quaternion
. En el siguiente fragmento de código de la demostración del teléfono de orientación, se muestra cómo se puede usar el sensor de orientación absoluto para rotar un modelo 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();
}
La orientación del dispositivo se reflejará en la rotación 3D model
dentro de la escena de WebGL.
Punchómetro
El siguiente fragmento de código se extrajo de la demostración del punchómetro, en el que se ilustra cómo se puede usar el sensor de aceleración lineal para calcular la velocidad máxima de un dispositivo bajo el supuesto de que inicialmente se encuentra quieto.
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;
};
La velocidad actual se calcula como una aproximación a la integral de la función de aceleración.
Depuración y anulación de sensores con las Herramientas para desarrolladores de Chrome
En algunos casos, no necesitas un dispositivo físico para jugar con la API de Generic Sensor. Las Herramientas para desarrolladores de Chrome son muy compatibles con la simulación de la orientación del dispositivo.
Privacidad y seguridad
Las lecturas de los sensores son datos sensibles que pueden estar sujetos a varios ataques de páginas web maliciosas. Las implementaciones de las APIs de Generic Sensor aplican algunas limitaciones para mitigar los posibles riesgos de seguridad y privacidad. Los desarrolladores que deseen usar la API deben tener en cuenta estas limitaciones, por lo que se debe enumerar brevemente.
Solo HTTPS
Como la API de Generic Sensor es una función potente, el navegador solo la permite en contextos seguros. En la práctica, significa que, para usar la API de Generic Sensor, necesitarás acceder a tu página a través de HTTPS. Durante el desarrollo, puedes hacerlo a través de http://localhost, pero, para la producción, deberás tener HTTPS en tu servidor. Consulta la colección Seguridad y protección para conocer las prácticas recomendadas y los lineamientos.
Integración de la política de permisos
La integración de políticas de permisos en la API de Generic Sensor controla el acceso a los datos de los sensores de una trama.
De forma predeterminada, los objetos Sensor
solo se pueden crear dentro de un marco principal o de submarcos del mismo origen, lo que evita que los iframes de origen cruzado puedan leer datos de sensores no autorizados. Este comportamiento predeterminado se puede modificar si habilitas o inhabilitas de forma explícita las funciones controladas por políticas correspondientes.
En el siguiente fragmento, se muestra cómo otorgar acceso a datos del acelerómetro a un iframe de origen cruzado, lo que significa que ahora se pueden crear objetos Accelerometer
o LinearAccelerationSensor
allí.
<iframe src="https://third-party.com" allow="accelerometer" />
Se puede suspender la entrega de lecturas del sensor
Solo se puede acceder a las lecturas del sensor mediante una página web visible, es decir, cuando el usuario está interactuando con él. Además, los datos del sensor no se proporcionarán al marco superior si el enfoque del usuario cambia a un submarco de origen cruzado. Esto evita que el marco superior infiera las entradas del usuario.
¿Qué sigue?
Existe un conjunto de clases de sensores ya especificadas que se implementarán en el futuro cercano, como el sensor de luz ambiente o el sensor de proximidad. Sin embargo, gracias a la gran extensibilidad del framework del sensor genérico, podemos anticipar la aparición de aún más clases nuevas que representen varios tipos de sensores.
Otra área importante de trabajo a futuro es mejorar la API de Generic Sensor. Por el momento, la especificación de Generic Sensor es una recomendación candidata, lo que significa que aún hay tiempo para hacer correcciones y ofrecer las nuevas funciones que los desarrolladores necesitan.
¡Puedes ayudar!
Las especificaciones del sensor alcanzaron el nivel de madurez de Candidate Recommendation, por lo que los comentarios de los desarrolladores web y de navegadores son muy apreciados. Cuéntanos qué funciones agregarías o si quisieras modificar algo en la API actual.
No dudes en informar problemas de especificación así como bugs de la implementación de Chrome.
Recursos
- Proyectos de demostración: https://intel.github.io/generic-sensor-demos/
- Especificación de la API de Sensor genérico: https://w3c.github.io/sensors/
- Problemas de especificación: https://github.com/w3c/sensors/issues
- Lista de distribución del grupo de trabajo del W3C: public-device-apis@w3.org
- Estado de las funciones de Chrome: https://www.chromestatus.com/feature/5698781827825664
- Errores de implementación: http://crbug.com?q=component:Blink>Sensor
Agradecimientos
Joe Medley y Kayce Basques revisaron este artículo. Imagen hero de Misko a través de Wikimedia Commons.