Use a API Sensor genérico para ter acesso a sensores no dispositivo, como acelerômetros, giroscópios e magnetômetros.
Atualmente, os dados do sensor são usados em muitos aplicativos específicos da plataforma para permitir casos de uso como jogos imersivos, monitoramento de condicionamento físico e realidade aumentada ou virtual. Não seria legal preencher a lacuna entre aplicativos específicos da plataforma e da Web? Entre na API Sensor genérico para a Web.
O que é a API Sensor genérica?
A API Sensor genérico é um conjunto de interfaces que expõem
dispositivos de sensores à plataforma da Web. A API consiste na interface
Sensor
e em um conjunto de classes
de sensores concretas criadas em cima. Ter uma interface de base simplifica a implementação e o processo
de especificação para as classes de sensores concretas. Por exemplo, confira a classe
Gyroscope
. É muito pequeno! A
funcionalidade principal é especificada pela interface de base, e Gyroscope
apenas a estende com três
atributos que representam a velocidade angular.
Algumas classes de sensores se conectam a sensores de hardware reais, como, por exemplo, as classes de acelerômetro ou
giroscópio. Eles são chamados de sensores de baixo nível. Outros sensores, chamados de
sensores de fusão, mesclam dados de vários sensores
de baixo nível para expor informações que um script precisaria calcular. Por exemplo, o sensor
AbsoluteOrientation
oferece uma matriz de rotação 4x4 pronta para uso com base nos dados recebidos do
acelerômetro, do giroscópio e do magnetômetro.
Você pode pensar que a plataforma da Web já fornece dados do sensor, e você está absolutamente certo! Por
exemplo, os eventos DeviceMotion
e
DeviceOrientation
expõem dados do sensor de movimento. Por que precisamos de uma nova API?
Em comparação com as interfaces atuais, a API Sensor genérico oferece um grande número de vantagens:
- A API Generic Sensor é um framework de sensores que pode ser facilmente estendido com novas classes de sensores, e cada uma dessas classes mantém a interface genérica. O código do cliente escrito para um tipo de sensor pode ser reutilizado para outro com poucas modificações.
- Você pode configurar o sensor. Por exemplo, é possível definir a frequência de amostragem adequada para suas necessidades de aplicativo.
- Você pode detectar se um sensor está disponível na plataforma.
- As leituras do sensor têm carimbos de data/hora de alta precisão, permitindo uma melhor sincronização com outras atividades no app.
- Os modelos de dados de sensores e os sistemas de coordenadas são claramente definidos, permitindo que os fornecedores de navegadores implementem soluções interoperáveis.
- As interfaces baseadas em sensores genéricos não são vinculadas ao DOM, ou seja, não são objetos
navigator
nemwindow
. Isso abre oportunidades futuras para usar a API em workers de serviço ou implementá-la em ambientes de execução JavaScript sem cabeça, como dispositivos integrados. - Os aspectos de segurança e privacidade são a principal prioridade da API Sensor genérico e oferecem uma segurança muito melhor em comparação com APIs de sensores mais antigas. Há integração com a API Permissions.
- A sincronização automática com coordenadas da tela está
disponível para
Accelerometer
,Gyroscope
,LinearAccelerationSensor
,AbsoluteOrientationSensor
,RelativeOrientationSensor
eMagnetometer
.
APIs de sensores genéricos disponíveis
No momento em que este artigo foi escrito, havia vários sensores que você podia testar.
Sensores de movimento:
Accelerometer
Gyroscope
LinearAccelerationSensor
AbsoluteOrientationSensor
RelativeOrientationSensor
GravitySensor
Sensores ambientais:
AmbientLightSensor
(atrás da flag#enable-generic-sensor-extra-classes
no Chromium)Magnetometer
(atrás da flag#enable-generic-sensor-extra-classes
no Chromium)
Detecção de recursos
A detecção de recursos de APIs de hardware é complicada, já que você precisa detectar se o navegador
oferece suporte à interface em questão e se o dispositivo tem o sensor correspondente. Verificar
se o navegador oferece suporte a uma interface é simples. Substitua Accelerometer
por qualquer
uma das outras interfaces mencionadas acima.
if ('Accelerometer' in window) {
// The `Accelerometer` interface is supported by the browser.
// Does the device have an accelerometer, though?
}
Para um resultado de detecção de recurso realmente significativo, você também precisa tentar se conectar ao sensor. Este exemplo ilustra como fazer isso.
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 navegadores que não oferecem suporte à API Sensor genérico, um polyfill está disponível. O polyfill permite carregar apenas as implementações de 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 });
O que são todos esses sensores? Como posso usá-los?
Os sensores são uma área que pode precisar de uma breve introdução. Se você já conhece os sensores, vá direto para a seção prática de programação. Caso contrário, vamos analisar cada sensor com suporte em detalhes.
Acelerômetro e sensor de aceleração linear
O sensor Accelerometer
mede a aceleração de um dispositivo que hospeda o sensor em três eixos (X, Y e Z). Esse sensor é um
sensor inercial, ou seja, quando o dispositivo está em queda livre linear, a aceleração total
medida é 0 m/s2, e quando um dispositivo está plano sobre uma mesa, a aceleração
na direção para cima (eixo Z) será igual à gravidade da Terra, ou seja, g ≈ +9,8 m/s2,
porque ele está medindo a força da mesa empurrando o dispositivo para cima. Se você empurrar o dispositivo para
a direita, a aceleração no eixo X será positiva ou negativa se o dispositivo for acelerado da
direita para a esquerda.
Os acelerômetros podem ser usados para coisas como contagem de passos, detecção de movimento ou orientação simples do dispositivo. Muitas vezes, as medições do acelerômetro são combinadas com dados de outras fontes para criar sensores de fusão, como sensores de orientação.
O
LinearAccelerationSensor
mede a aceleração aplicada ao dispositivo que hospeda o sensor, excluindo a contribuição
da gravidade. Quando um dispositivo está em repouso, por exemplo, posicionado com a tela para cima sobre uma mesa, o sensor mede
≈ 0 m/s2 de aceleração nos três eixos.
Sensor de gravidade
Já é possível que os usuários extraiam manualmente leituras próximas às de um sensor de gravidade
inspecionando manualmente as leituras de Accelerometer
e LinearAccelerometer
, mas isso pode ser complicado
e depender da precisão dos valores fornecidos por esses sensores. Plataformas como o Android podem
fornecer leituras de gravidade como parte do sistema operacional, que devem ser mais baratas em termos de
computação, fornecer valores mais precisos dependendo do hardware do usuário e ser mais fáceis de usar em
termos de ergonomia da API. O
GravitySensor
retorna o efeito
da aceleração ao longo dos eixos X, Y e Z do dispositivo devido à gravidade.
Giroscópio
O sensor Gyroscope
mede
a velocidade angular em radianos por segundo em torno dos eixos X, Y e Z locais do dispositivo. A maioria dos dispositivos
de consumo tem giroscópios mecânicos (MEMS),
que são sensores inerciais que medem a taxa de rotação com base na
força inercial de Coriolis. Os giroscópios MEMS são propensos
a desvios causados pela sensibilidade gravitacional do sensor, que deforma o sistema mecânico
interno do sensor. Os giroscópios oscilam em frequências relativamente altas, por exemplo, 10 kHz e, portanto, pode consumir mais energia em comparação com outros sensores.
Sensores de orientação
O
AbsoluteOrientationSensor
é um sensor de fusão que mede a rotação de um dispositivo em relação ao sistema de coordenadas da Terra,
enquanto o
RelativeOrientationSensor
fornece dados que representam a rotação de um dispositivo que hospeda sensores de movimento em relação a um sistema de coordenadas de referência
estacionário.
Todos os frameworks modernos do JavaScript 3D oferecem suporte a quaterniões
e matrizes de rotação para representar a rotação.
No entanto, se você usar o WebGL diretamente, o OrientationSensor
terá uma
propriedade quaternion
e um
método populateMatrix()
.
Confira alguns exemplos:
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);
Os sensores de orientação permitem vários casos de uso, como jogos imersivos, realidade aumentada e virtual.
Para mais informações sobre sensores de movimento, casos de uso avançados e requisitos, consulte o documento Explicação sobre sensores de movimento.
Sincronização com coordenadas da tela
Por padrão, as leituras de sensores espaciais são resolvidas em um sistema de coordenadas local vinculado ao dispositivo e não consideram a orientação da tela.
No entanto, muitos casos de uso, como jogos ou realidade aumentada e virtual, exigem que as leituras do sensor sejam resolvas em um sistema de coordenadas vinculado à orientação da tela.
Antes, a remapeação das leituras do sensor para coordenadas da tela precisava ser implementada no JavaScript. Essa abordagem é ineficiente e também aumenta significativamente a complexidade do código do aplicativo da Web. O aplicativo da Web precisa monitorar as mudanças de orientação da tela e realizar transformações de coordenadas para leituras de sensores, o que não é uma coisa trivial para ângulos de Euler ou quaterniões.
A API Sensor genérico oferece uma solução muito mais simples e confiável. O sistema de coordenadas local é
configurável para todas as classes de sensores espaciais definidas: Accelerometer
, Gyroscope
,
LinearAccelerationSensor
, AbsoluteOrientationSensor
, RelativeOrientationSensor
e
Magnetometer
. Ao transmitir a opção referenceFrame
para o construtor do objeto do sensor, o usuário
define se as leituras retornadas serão resolvidas nas coordenadas do
dispositivo ou da
tela.
// 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' });
Vamos programar!
A API Sensor genérico é muito simples e fácil de usar. A interface do sensor tem os métodos
start()
e
stop()
para controlar o estado do sensor e vários
gerenciadores de eventos para receber notificações sobre a ativação do sensor, erros e leituras
recém-disponíveis. As classes de sensores concretas geralmente adicionam os atributos de leitura específicos à classe
base.
Ambiente de desenvolvimento
Durante o desenvolvimento, você poderá usar sensores com localhost
. Se você estiver desenvolvendo para
dispositivos móveis, configure o
encaminhamento de portas
para seu servidor local e pronto.
Quando o código estiver pronto, implante-o em um servidor com suporte a HTTPS. As páginas do GitHub são veiculadas por HTTPS, o que as torna um ótimo lugar para compartilhar suas demonstrações.
Rotação do modelo 3D
Neste exemplo simples, usamos os dados de um sensor de orientação absoluta para modificar o quaternion de rotação
de um modelo 3D. O model
é uma instância da classe
Object3D
do three.js que tem uma propriedade
quaternion
. O
snippet de código abaixo da demonstração
orientação do smartphone
ilustra como o sensor de orientação absoluto pode ser usado para girar um 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();
}
A orientação do dispositivo será refletida na rotação model
em 3D na cena do WebGL.
Punchmeter
O snippet de código a seguir é extraído da demonstração do velocímetro, ilustrando como o sensor de aceleração linear pode ser usado para calcular a velocidade máxima de um dispositivo, supondo que ele esteja inicialmente parado.
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;
};
A velocidade atual é calculada como uma aproximação da integral da função de aceleração.
Depuração e substituição de sensores com o Chrome DevTools
Em alguns casos, você não precisa de um dispositivo físico para usar a API Generic Sensor. O Chrome DevTools tem um ótimo suporte para simular a orientação do dispositivo.
Privacidade e segurança
As leituras de sensores são dados sensíveis que podem estar sujeitos a vários ataques de páginas da Web maliciosas. As implementações de APIs de sensor genéricas impõem algumas limitações para reduzir os possíveis riscos de segurança e privacidade. Essas limitações precisam ser consideradas pelos desenvolvedores que pretendem usar a API. Confira uma lista delas.
Somente HTTPS
Como a API Generic Sensor é um recurso poderoso, o navegador só permite que ela seja usada em contextos seguros. Na prática, isso significa que, para usar a API Generic Sensor, você precisa acessar sua página pelo HTTPS. Durante o desenvolvimento, é possível fazer isso por http://localhost, mas para a produção, será necessário ter o HTTPS no servidor. Consulte a coleção Segurança para conferir práticas recomendadas e diretrizes.
Integração da Política de permissões
A integração da política de permissões na API Sensor genérico controla o acesso aos dados dos sensores de um frame.
Por padrão, os objetos Sensor
só podem ser criados em um frame principal ou subframes de mesma origem,
impedindo que iframes de origem cruzada leiam dados de sensores sem autorização. Esse comportamento padrão
pode ser modificado ativando ou desativando explicitamente os
recursos controlados por política correspondentes.
O snippet abaixo ilustra o acesso de dados do acelerômetro a um iframe de origem cruzada, o que significa
que agora os objetos Accelerometer
ou LinearAccelerationSensor
podem ser criados nele.
<iframe src="https://third-party.com" allow="accelerometer" />
A entrega de leituras de sensores pode ser suspensa
As leituras do sensor só são acessíveis por uma página da Web visível, ou seja, quando o usuário está interagindo com ela. Além disso, os dados do sensor não seriam fornecidos ao frame pai se o foco do usuário mudasse para um subframe de origem cruzada. Isso impede que o frame pai infira a entrada do usuário.
A seguir
Há um conjunto de classes de sensores já especificados para serem implementadas em breve, como Sensor de luz ambiente ou Sensor de proximidade. No entanto, graças à grande extensibilidade do framework de sensor genérico, podemos antecipar a aparência de ainda mais novas classes que representam vários tipos de sensores.
Outra área importante para o trabalho futuro é melhorar a própria API Sensor genérico. A especificação do Sensor genérico é atualmente uma recomendação candidata, o que significa que ainda há tempo para fazer correções e trazer novas funcionalidades que os desenvolvedores precisam.
Você pode ajudar.
As especificações do sensor alcançaram o nível de maturidade recomendação de candidato, portanto, o feedback dos desenvolvedores da Web e do navegador é muito apreciado. Informe quais recursos você gostaria de adicionar ou se há algo que gostaria de modificar na API atual.
Sinta-se à vontade para enviar problemas de especificação e bugs para a implementação do Chrome.
Recursos
- Projetos de demonstração: https://intel.github.io/generic-sensor-demos/
- Especificação da API Sensor genérica: https://w3c.github.io/sensors/
- Problemas de especificação: https://github.com/w3c/sensors/issues
- Lista de e-mails do grupo de trabalho do W3C: public-device-apis@w3.org
- Status do recurso do Chrome: https://www.chromestatus.com/feature/5698781827825664
- Bugs de implementação: http://crbug.com?q=component:Blink>Sensor
Agradecimentos
Este artigo foi revisado por Joe Medley e Kayce Basques.