Web 传感器

使用通用传感器 API 访问设备端传感器,例如加速度计、陀螺仪和磁力计。

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

目前,传感器数据在许多平台专用应用中都有使用,以实现沉浸式游戏、健身跟踪以及增强现实或虚拟现实等用例。如果能够跨平台原生应用和 Web 应用之间的鸿沟,那将会很酷。欢迎使用适用于 Web 的 Generic Sensor API

什么是通用传感器 API?

通用传感器 API 是一组接口,用于公开 将传感器设备传输到 Web 平台。该 API 由基础 Sensor 接口和一组基于该接口构建的具体传感器类组成。拥有基接口可以简化实现和规范 了解具体传感器类的整个流程。例如,请查看 Gyroscope 类。它超小了!通过 核心功能由基接口指定,Gyroscope 只是对其使用三个 属性。

某些传感器类与实际的硬件传感器接口,如加速度计或 陀螺仪类。这些传感器称为低级传感器。其他传感器,也称为 融合传感器,用于合并来自多个低级别的数据 传感器来公开脚本本来需要计算的信息。例如,AbsoluteOrientation 传感器会根据从加速度计、陀螺仪和磁力计获取的数据提供一个随时可用的 4 x 4 旋转矩阵。

您可能会认为网络平台已经提供传感器数据,您完全正确!例如,DeviceMotionDeviceOrientation 事件会公开动作传感器数据。那么,为什么我们需要新的 API?

与现有接口相比,通用传感器 API 具有诸多优势:

  • Generic Sensor API 是一种传感器框架,可通过新的传感器类轻松扩展,并且每个类都将保留通用接口。为一种传感器类型编写的客户端代码 可以重复利用,只需极少的修改即可!
  • 您可以配置传感器。例如,您可以设置适合应用需求的采样频率。
  • 您可以检测平台上是否有传感器。
  • 传感器读数具有高精度时间戳,能够更好地与其他设备同步 activity。
  • 传感器数据模型和坐标系已明确定义,让浏览器供应商能够实现可互操作的解决方案。
  • 基于通用传感器的接口未绑定到 DOM(这意味着它们既不是 navigator) 也不是 window 对象),这为在服务中使用 API 开创了未来机会 或是在无头 JavaScript 运行时中实现它,例如 设备。
  • 安全和隐私方面是通用传感器的首要任务 API 并提供更出色的安全性(与旧版传感器 API 相比)。它与 权限 API。
  • 与屏幕坐标同步 适用于 AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorMagnetometer

可用的通用传感器 API

在撰写本文时,您可以尝试多种传感器。

移动传感器

  • Accelerometer
  • Gyroscope
  • LinearAccelerationSensor
  • AbsoluteOrientationSensor
  • RelativeOrientationSensor
  • GravitySensor

环境传感器

  • AmbientLightSensor(位于 Chromium 中的 #enable-generic-sensor-extra-classes 标志后面。)
  • Magnetometer(位于 Chromium 中的 #enable-generic-sensor-extra-classes 标志后面)。

功能检测

硬件 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 的浏览器, polyfill。借助此 polyfill,您可以仅加载相关传感器的实现。

// 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 m/s2,当设备平放在桌子上时,加速度 向上的方向(Z 轴)等于地球重力,即 g ~ +9.8 m/s2, 而是要测量桌子将设备向上推的力度如果您将设备推送到 则 X 轴上的加速度为正,如果设备从以下时间加速: 从右向左。

加速度计可用于计步、移动感应或简单设备等用途 屏幕方向。通常,加速度计测量结果会与来自其他来源的数据结合使用,以创建融合传感器,例如屏幕方向传感器。

LinearAccelerationSensor 用于测量施加到托管传感器的设备的加速度,不包括重力的影响。当设备处于静止状态(例如平放在桌子上)时,传感器会测量三个轴上的加速度,结果约为 0 m/s2

重力传感器

用户已经可以通过手动检查 AccelerometerLinearAccelerometer 读数,手动推导出接近重力传感器读数的读数,但这可能很麻烦,并且取决于这些传感器提供的值的准确性。Android 等平台 作为操作系统的一部分提供重力读数 根据用户的硬件提供更准确的值,并且在 API 工效学方面的专业知识。GravitySensor 会返回重力对设备 X、Y 和 Z 轴加速度的影响。

陀螺仪

<ph type="x-smartling-placeholder">
</ph>
陀螺仪传感器测量

Gyroscope 传感器会测量 围绕设备的局部 X、Y 和 Z 轴的角速度(以弧度/秒为单位)。消费者最多 具有机械 (MEMS) 的设备 陀螺仪,也就是惯性传感器,可根据 惯性科里奥利力。MEMS 陀螺仪容易出现漂移,这是由传感器的引力灵敏度导致的,该灵敏度会使传感器的内部机械系统变形。陀螺仪以相对高频率振荡,例如,几十 kHz,因此与其他传感器相比,功耗可能会更高。

屏幕方向传感器

<ph type="x-smartling-placeholder">
</ph>
绝对方向传感器测量结果

AbsoluteOrientationSensor 是一种融合传感器,用于测量设备相对于地球坐标系的旋转;而 RelativeOrientationSensor 则提供表示托管运动传感器的设备相对于静态参考坐标系的旋转的数据。

所有现代 3D JavaScript 框架都支持四元数旋转矩阵来表示旋转; 不过,如果您直接使用 WebGL,则 OrientationSensor 会非常方便 quaternion 媒体资源populateMatrix() 方法。 下面是一些代码段:

three.js

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();

BABYLON

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();

WebGL

// 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);

屏幕方向传感器支持各种应用场景,例如沉浸式游戏、增强和虚拟

如需详细了解移动传感器、高级用例和要求,请参阅移动传感器说明文档

与屏幕坐标同步

默认情况下,系统会解析空间传感器的读数 本地坐标系中,该坐标系绑定到设备,并且不将屏幕方向 。

设备坐标系
设备坐标系

不过,许多使用情形(例如游戏或增强现实和虚拟现实)都需要在与屏幕方向绑定的坐标系中解析传感器读数。

<ph type="x-smartling-placeholder">
</ph> 屏幕坐标系
屏幕坐标系

以前,必须将传感器读数重新映射到屏幕坐标,而且必须使用 JavaScript 实现。 这种方法效率低下,而且还会显著增加 Web 的复杂性 应用代码;Web 应用必须观察屏幕方向变化,并执行坐标 进行传感器读数转换,对于欧拉角或 四元数。

Generic Sensor API 提供了更简单、更可靠的解决方案!本地坐标系可针对所有已定义的空间传感器类进行配置:AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorMagnetometer。将 referenceFrame 选项传递给传感器对象构造函数后,用户 定义返回的读数是否在 device屏幕坐标。

// 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' });

开始编码吧!

Generic Sensor API 非常简单易用!Sensor 接口具有 start()stop() 方法来控制传感器状态,以及多个事件处理脚本来接收有关传感器激活、错误和新可用读数的通知。具体的传感器类通常会将其特定的读取属性添加到基本 类。

开发环境

在开发过程中,您可以通过 localhost 使用传感器。如果您要针对移动设备进行开发,请为本地服务器设置端口转发,然后即可大展拳脚了!

代码准备就绪后,将其部署到支持 HTTPS 的服务器上。GitHub 页面采用 HTTPS 协议,是分享演示的绝佳平台。

3D 模型旋转

在此简单示例中,我们使用绝对方向传感器中的数据来修改 3D 模型的旋转四元数。model 是具有 quaternion 属性的 three.js Object3D 类实例。手机屏幕方向演示中的以下代码段展示了如何使用绝对方向传感器旋转 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();
}

设备的屏幕方向将反映在 WebGL 场景中的 3D model 旋转中。

传感器更新 3D 模型的方向
传感器更新 3D 模型的方向

冲压计

以下代码段摘自冲击力计演示,展示了如何假设设备最初处于静止状态,然后使用线性加速度传感器计算设备的最大速度。

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;
};

当前速度的计算方法是近似于加速度函数积分。

用于测量冲击速度的演示 Web 应用
测量冲击速度

使用 Chrome 开发者工具进行调试和传感器替换

在某些情况下,您无需实体设备即可使用通用传感器 API。Chrome 开发者工具 提供了强大的支持 模拟设备屏幕方向

Chrome DevTools 用于替换虚拟手机的自定义屏幕方向数据
使用 Chrome DevTools 模拟设备屏幕方向

隐私权和安全

传感器读数属于敏感数据,可能会受到恶意网页的各种攻击。通用传感器 API 的实现会强制实施一些限制,以降低可能的安全性 和隐私风险打算使用该 API 的开发者必须考虑这些限制,因此我们来简要列出这些限制。

仅限 HTTPS

由于通用传感器 API 是一项强大的功能,因此浏览器仅允许在安全情境中使用该 API。实际上,这意味着若要使用 Generic Sensor API,您需要通过 HTTPS 访问网页。在开发期间,您可以通过 http://localhost 进行测试,但对于生产环境,您需要在服务器上启用 HTTPS。如需了解最佳做法,请参阅安全集合 和准则。

“权限”政策集成

通用中的权限政策集成 传感器 API 控制对帧的传感器数据的访问。

默认情况下,只能在主框架或同源子框架中创建 Sensor 对象, 从而防止跨源 iframe 在未经批准的情况下读取传感器数据。这种默认行为 来修改代码, 政策控制的功能

以下代码段说明了如何向跨源 iframe 授予加速度计数据访问权限,这意味着 现在可以在其中创建 AccelerometerLinearAccelerationSensor 对象。

<iframe src="https://third-party.com" allow="accelerometer" />

可以暂停传送传感器读数

只有可见的网页(即用户实际与之互动时)才能访问传感器读数。此外,如果用户焦点更改为跨源子帧,系统将不会向父帧提供传感器数据。这可以防止父框架推断用户输入。

后续操作

我们即将实现一组已指定的传感器类,例如环境光传感器距离传感器;不过,得益于通用传感器框架的强大可扩展性,我们预计还会出现更多代表各种传感器类型的新类。

未来工作的另一个重要领域是改进通用传感器 API 本身,即通用传感器, 传感器规范目前是候选建议,这意味着仍然有时间 修复问题并引入开发者需要的新功能。

您可以提供帮助!

已达到传感器规格 候选建议 成熟度水平,因此,我们非常重视 Web 和浏览器开发者的反馈。让我们 知道哪些功能会很棒,或者想在 当前 API。

如有需要,请随时针对 Chrome 实现提交规范问题 bug

资源

致谢

本文由 Joe MedleyKayce Basques。主打图片:Misko 通过 Wikimedia Commons 提供。