網頁感應器

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

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

如今,許多平台專用的應用程式都使用感應器資料,以實現沉浸式遊戲、健身追蹤,以及擴增或虛擬實境等用途。如果想消弭平台專屬應用程式與網頁應用程式之間的差距 是不是很好呢?輸入「Generic Sensor API」(一般感應器 API) 來在網路上!

什麼是 Generic Sensor API?

「Generic Sensor API」是一組介面,會向網路平台公開感應器裝置。API 包含基本 Sensor 介面和一組以為基礎的具體感應器類別。建立基本介面可以簡化具體感應器類別的實作和規格程序。舉例來說,請查看 Gyroscope 類別。它非常小!核心功能由基本介面指定,Gyroscope 只會透過三個屬性表示角速度。

某些感應器類別介面與實際硬體感應器的介面,例如加速計或陀螺儀類別。稱為低階感應器。其他感應器 (稱為「融合感應器」) 會合併多個低階感應器的資料,以揭露指令碼需要計算的資訊。舉例來說,AbsoluteOrientation 感應器根據透過加速計、陀螺儀和磁力儀取得的資料,提供可立即使用的四輪旋轉矩陣。

你可能會以為網路平台已提供感應器資料,而你完全是對的!舉例來說,DeviceMotionDeviceOrientation 事件揭露動作感應器資料。為什麼需要新的 API?

與現有介面相比,一般感應器 API 可提供許多優點:

  • 泛型感應器 API 是一種可透過新的感應器類別輕鬆擴充的感應器架構,且每個類別都將保留一般介面。為某個感應器類型編寫的用戶端程式碼,只要稍微修改一下,就能重複用於其他感應器!
  • 您可以設定感應器。例如,您可以設定符合應用程式需求的取樣頻率。
  • 你可以偵測平台上是否有感應器。
  • 感應器讀取作業具有高精確度的時間戳記,能夠更好地與應用程式中的其他活動同步。
  • 已明確定義感應器資料模型和座標系統,方便瀏覽器廠商導入可互通的解決方案。
  • 一般感應器式介面不會與 DOM 繫結 (亦即這些介面不是 navigatorwindow 物件),而這也讓您未來有機會在服務工作站內使用 API,或是在無頭 JavaScript 執行階段 (例如嵌入式裝置) 中使用 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;
  }
}

聚酯纖維

如果瀏覽器不支援泛用感應器 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 軸) 的加速度等於地球的重力 (Z 軸) 等於地球的重力表,也就是向上推至向上表 9.8 平方公尺的程度。如果將裝置向右推,X 軸的加速度會是正值,如果裝置由右側向右加速,X 軸的加速度會是負數。

加速計可用於計算步數、動作感應或簡單的裝置方向等用途。一般而言,加速計測量結果會與其他來源的資料結合,建立融合感應器 (例如方向感應器)。

LinearAccelerationSensor 會測量套用至感應器的裝置所適用的加速速度 (不包括重力影響)。當裝置處於靜止狀態時 (例如平放在桌子上),感應器會以三軸測量 KB m/s2 加速度。

重力感應器

已允許使用者手動檢查 AccelerometerLinearAccelerometer 讀數,手動找出接近重力感應器的讀數,但這可能很麻煩,且需視這些感應器提供的值準確度而定。Android 等平台可在作業系統中提供重力讀數,這會降低計算成本,並根據使用者的硬體提供更準確的值,且更易於在 API 人體工學中使用。GravitySensor 會根據重力,傳回沿著裝置的 X、Y 和 Z 軸加速度的效果。

陀螺儀

陀螺儀感應器測量

Gyroscope 感應器可以測量裝置本機 X、Y 和 Z 軸周圍的角速度,以每秒弧度為單位。大多數消費者裝置都具備機械 (MEMS) 陀螺儀,這種模擬感應器可根據象限 Coriolis 力測量旋轉速率。MEMS 陀螺儀容易偏移,因為感應器的重力敏感度會變形感應器的內部機械系統。陀螺儀以相對頻率上升,例如10 的 kHz 的耗電量。因此,相較於其他感應器,耗電量可能更多。

方向感應器

絕對方向感應器測量

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 中實作將感應器讀數重新對應至螢幕座標。這種做法效率不彰,也會大幅增加網頁應用程式程式碼的複雜度;網頁應用程式必須監控螢幕方向變更,並執行感應器讀取作業的座標轉換,但這對於尤爾角或四元數來說來說不太容易。

泛型感應器 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' });

來編寫程式碼吧!

Generic Sensor API 非常簡單且容易使用!感應器介面提供 start()stop() 方法,可控制感應器狀態,以及多個事件處理常式,用於接收感應器啟用、錯誤和最新讀取內容相關通知。具體感應器類別通常會將專屬的讀取屬性加入基本類別。

開發環境

在開發期間,你可以透過 localhost 使用感應器。如果您要針對行動裝置進行開發,請為本機伺服器設定通訊埠轉送,之後就能盡情搖身一變!

程式碼準備就緒之後,部署至支援 HTTPS 的伺服器。GitHub 頁面是透過 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 示範中擷取的資訊,說明如何利用線性加速感應器,以假設一開始仍在靜止不動的情況下,使用線性加速感應器來計算裝置最大速率。

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 是強大的功能,因此瀏覽器僅允許在安全的內容中使用該 API。實際上,您必須使用 HTTPS 存取網頁才能使用泛用感應器 API。 在開發期間,您可以透過 http://localhost 進行,但對於正式環境,您需要在伺服器上設定 HTTPS。如要瞭解最佳做法和規範,請參閱「安全保障」集合。

權限政策整合

一般感應器 API 中的權限政策整合可控管影格的感應器資料存取權。

根據預設,Sensor 物件只能在主頁框或相同來源子頁框中建立,因此可防止跨來源 iframe 在未經審核的情況下讀取感應器資料。如要修改這項預設行為,您可以明確啟用或停用對應的由政策控管功能

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

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

可暫停傳送感應器讀數

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

後續步驟

我們會在不久後實作一組指定的感應器類別,例如環境光度感應器鄰近感應器。不過,由於一般感應器架構的擴充能力非常大,我們預期我們就能預期會推出更多代表不同感應器類型的新類別。

一般感應器規格目前是日後工作的另一項重要重點,也就是一般感應器規格目前為候選建議項目,這表示還有時間進行修正,以及提供開發人員需要的新功能。

可以幫上忙!

感應器規格達到候選建議的成熟度等級,因此非常感謝網頁與瀏覽器開發人員提供的意見。讓我們知道哪些功能適合新增,或是在目前的 API 中有任何需要修改的內容。

歡迎您針對 Chrome 實作方式提出規格問題bugs

資源

特別銘謝

本文由 Joe MedleyKayce Basques 審查。Misko 透過 Wikimedia Commons 提供的主頁橫幅。