網頁感應器

使用 Generic Sensor API 取得裝置端感應器,例如加速計、陀螺儀和磁力儀。

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

如今,感應器資料已廣泛應用於許多平台專屬應用程式,用於沉浸式遊戲、健身追蹤,以及擴增或虛擬實境等用途。將特定平台和網頁應用程式之間的差距縮小,這不是很棒嗎?輸入適用於網路的 通用感應器 API

什麼是 Generic Sensor API?

Generic Sensor API 是一組介面,可將感應器裝置公開給網路平台。這個 API 包含基本 Sensor 介面,以及一組在上層建構的具體感應器類別。有了基本介面,就能簡化具體感應器類別的實作和規格程序。舉例來說,請查看 Gyroscope 類別。它超級小!核心功能由基礎介面指定,而 Gyroscope 只會使用三個代表角速度的屬性擴充功能。

有些感應器類別會與實際的硬體感應器連結,例如加速計或陀螺儀類別。這些稱為低層級感應器。其他感應器 (稱為融合感應器) 會合併多個低階感應器的資料,以便提供指令碼原本需要計算的資訊。舉例來說,AbsoluteOrientation 感應器會根據加速計、陀螺儀和磁力計取得的資料,提供立即可用的四輪旋轉矩陣。

您或許認為網路平台已經提供感應器資料,您絕對沒錯!舉例來說,DeviceMotionDeviceOrientation 事件會公開動作感應器資料。那麼,為什麼我們需要新的 API?

與現有的介面相比,通用感應器 API 提供許多優點:

  • 通用感應器 API 是一種感應器架構,可輕鬆擴充新的感應器類別,且每個類別都會保留通用介面。為一種感應器類型編寫的用戶端程式碼,只要稍加修改,就能重複用於其他感應器類型。
  • 您可以設定感應器。舉例來說,您可以設定適合應用程式需求的取樣頻率。
  • 您可以偵測平台上是否有感應器。
  • 感應器讀數具有高精確度的時間戳記,可與應用程式中的其他活動進行更佳同步。
  • 感應器資料模型和座標系統已明確定義,讓瀏覽器供應商能夠導入可互通的解決方案。
  • 一般感應器型介面不會繫結至 DOM (也就是說這些介面不是 navigatorwindow 物件),這會開啟未來在服務 worker 中使用 API 的機會,或是在無頭 JavaScript 執行階段 (例如內嵌裝置) 中實作該 API 的機會。
  • 安全性和隱私權是通用感應器 API 的首要考量,與舊版感應器 API 相比,通用感應器 API 提供更完善的安全性。已整合 Permissions 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;
  }
}

聚合物

如果瀏覽器不支援 Generic Sensor 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 軸的加速度效果。

陀螺儀

陀螺儀感應器測量結果

Gyroscope 感應器會以每秒弧度為單位,測量裝置本地 X、Y 和 Z 軸的角速度。大多數消費性裝置都配備機械 (MEMS) 陀螺儀,這是根據慣性科目力測量旋轉速率的初始感應器。MEMS 陀螺儀容易出現漂移現象,這是因為感應器的引力靈敏度會使感應器的內部機械系統變形。陀螺儀會以相對較高的頻率震動,例如因此,相較於其他感應器,可能會耗用更多電力。

方向感應器

絕對方向感應器測量結果

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

方向感應器支援多種用途,例如沉浸式遊戲、擴增和虛擬實境。

如要進一步瞭解動作感應器、進階用途和相關規定,請參閱動作感應器說明文件。

與螢幕座標同步

根據預設,系統會在繫結裝置的本機座標系統中解析空間感應器的讀數,但不會將螢幕方向納入考量。

裝置座標系統
裝置座標系統

不過,許多用途 (例如遊戲或擴增和虛擬實境) 都需要在與螢幕方向綁定的座標系統中解析感應器讀數。

螢幕座標系統
螢幕座標系統

先前,您必須使用 JavaScript 將感應器讀數重新對應至螢幕座標。這種做法效率不彰,而且會大幅增加網頁應用程式程式碼的複雜度;網頁應用程式必須監控螢幕方向變更,並為感應器讀數執行座標轉換,而這對於歐拉角或四元數來說,並非易事。

Generic Sensor API 提供更加簡單可靠的解決方案!當地座標系統可針對所有定義的空間感應器類別進行設定:AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorMagnetometer。將 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 非常簡單易用!感應器介面提供 start()stop() 方法,可控制感應器狀態,以及用於接收感應器啟動、錯誤和新讀取通知通知的多個事件處理常式。具體感應器類別通常會將特定讀取屬性新增至基礎類別。

開發環境

在開發期間,您可以透過 localhost 使用感應器。如果您是針對行動裝置進行開發,請為本機伺服器設定通訊埠轉送,就能開始使用!

程式碼準備就緒後,請將程式碼部署至支援 HTTPS 的伺服器。GitHub Pages 是透過 HTTPS 提供,因此非常適合用來分享示範。

3D 模型旋轉

在這個簡單的範例中,我們會使用絕對方向感應器的資料,修改 3D 模型的旋轉四元數。model 是包含 quaternion 屬性的 3.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 模型的方向

Punchmeter

下列程式碼片段是從 punchmeter 示範中擷取,說明如何在假設裝置一開始處於靜止狀態的情況下,使用線性加速度感應器來計算裝置的最高速度。

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 開發人員工具進行偵錯和感應器覆寫

在某些情況下,您不需要實體裝置就能使用 Generic Sensor API。Chrome 開發人員工具可提供模擬裝置方向的絕佳支援。

Chrome 開發人員工具,用於覆寫虛擬手機的自訂方向資料
使用 Chrome 開發人員工具模擬裝置方向

隱私權與安全性

感應器讀數屬於機密資料,可能會遭受惡意網頁的各種攻擊。通用感應器 API 的實作會強制執行一些限制,以降低可能的安全性和隱私權風險。有意使用 API 的開發人員必須考量這些限制,因此讓我們簡單列出這些限制。

僅限 HTTPS

由於 Generic Sensor API 是強大的功能,瀏覽器只能在安全的情況下使用。換句話說,如要使用 Generic Sensor API,您必須透過 HTTPS 存取網頁。在開發期間,您可以透過 http://localhost 執行這項操作,但在實際工作環境中,則需要在伺服器採用 HTTPS。如需最佳做法和指南,請參閱安全無虞系列。

權限政策整合

通用感應器 API 中的權限政策整合功能,可控管影格感應器資料的存取權。

根據預設,Sensor 物件只能在主框架或同源子框架中建立,因此可防止跨來源 iframe 未經授權就讀取感應器資料。您可以明確啟用或停用對應的政策控管功能,藉此修改這項預設行為。

下方程式碼片段說明如何授予跨來源 iframe 的加速計資料存取權,代表現在您可以在該處建立 AccelerometerLinearAccelerationSensor 物件。

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

可暫停傳送感應器讀數

只有可見的網頁 (也就是使用者實際與網頁互動時) 才能存取感應器讀數。此外,如果使用者焦點變更為跨來源子影格,系統就不會將感應器資料提供給父項影格。這樣可以防止上層頁框推斷使用者輸入內容。

後續步驟

在近期,我們將實作一組已指定的感應器類別,例如環境光度感應器鄰近感應器;不過,由於通用感應器架構的可擴充性極佳,我們可以預期會有更多代表各種感應器類型的新類別出現。

未來另一個重要的工作領域,是改善通用感應器 API 本身。通用感應器規格目前是候選推薦規格,也就是說,我們仍有時間進行修正,並提供開發人員需要的新功能。

讓我們來幫忙!

感應器規格已達到候選推薦的成熟度,因此非常歡迎網頁和瀏覽器開發人員提供意見回饋。請告訴我們您認為哪些功能很適合加入,或是您想修改目前 API 中的哪些內容。

歡迎您針對 Chrome 實作項目提出規格問題錯誤

資源

特別銘謝

本文由 Joe MedleyKayce Basques 審查。