使用 Generic Sensor API 取得裝置端感應器,例如加速計、陀螺儀和磁力儀。
如今,感應器資料已廣泛應用於許多平台專屬應用程式,用於沉浸式遊戲、健身追蹤,以及擴增或虛擬實境等用途。將特定平台和網頁應用程式之間的差距縮小,這不是很棒嗎?輸入適用於網路的 通用感應器 API!
什麼是 Generic Sensor API?
Generic Sensor API 是一組介面,可將感應器裝置公開給網路平台。這個 API 包含基本 Sensor
介面,以及一組在上層建構的具體感應器類別。有了基本介面,就能簡化具體感應器類別的實作和規格程序。舉例來說,請查看 Gyroscope
類別。它超級小!核心功能由基礎介面指定,而 Gyroscope
只會使用三個代表角速度的屬性擴充功能。
有些感應器類別會與實際的硬體感應器連結,例如加速計或陀螺儀類別。這些稱為低層級感應器。其他感應器 (稱為融合感應器) 會合併多個低階感應器的資料,以便提供指令碼原本需要計算的資訊。舉例來說,AbsoluteOrientation
感應器會根據加速計、陀螺儀和磁力計取得的資料,提供立即可用的四輪旋轉矩陣。
您或許認為網路平台已經提供感應器資料,您絕對沒錯!舉例來說,DeviceMotion
和 DeviceOrientation
事件會公開動作感應器資料。那麼,為什麼我們需要新的 API?
與現有的介面相比,通用感應器 API 提供許多優點:
- 通用感應器 API 是一種感應器架構,可輕鬆擴充新的感應器類別,且每個類別都會保留通用介面。為一種感應器類型編寫的用戶端程式碼,只要稍加修改,就能重複用於其他感應器類型。
- 您可以設定感應器。舉例來說,您可以設定適合應用程式需求的取樣頻率。
- 您可以偵測平台上是否有感應器。
- 感應器讀數具有高精確度的時間戳記,可與應用程式中的其他活動進行更佳同步。
- 感應器資料模型和座標系統已明確定義,讓瀏覽器供應商能夠導入可互通的解決方案。
- 一般感應器型介面不會繫結至 DOM (也就是說這些介面不是
navigator
和window
物件),這會開啟未來在服務 worker 中使用 API 的機會,或是在無頭 JavaScript 執行階段 (例如內嵌裝置) 中實作該 API 的機會。 - 安全性和隱私權是通用感應器 API 的首要考量,與舊版感應器 API 相比,通用感應器 API 提供更完善的安全性。已整合 Permissions API。
Accelerometer
、Gyroscope
、LinearAccelerationSensor
、AbsoluteOrientationSensor
、RelativeOrientationSensor
和Magnetometer
可自動與螢幕座標同步。
可用的一般感應器 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 的加速度。
重力感應器
使用者可能已手動檢查 Accelerometer
和 LinearAccelerometer
讀數,手動取得靠近重力感應器的讀數,但這種做法可能相當麻煩,還須視這些感應器提供的值之準確度而定。Android 等平台可在作業系統中提供重力讀數 (應更便宜),根據使用者的硬體提供更準確的值,而在 API 人體工學方面也較容易使用。GravitySensor
會傳回因重力而沿著裝置 X、Y 和 Z 軸的加速度效果。
陀螺儀
Gyroscope
感應器會以每秒弧度為單位,測量裝置本地 X、Y 和 Z 軸的角速度。大多數消費性裝置都配備機械 (MEMS) 陀螺儀,這是根據慣性科目力測量旋轉速率的初始感應器。MEMS 陀螺儀容易出現漂移現象,這是因為感應器的引力靈敏度會使感應器的內部機械系統變形。陀螺儀會以相對較高的頻率震動,例如因此,相較於其他感應器,可能會耗用更多電力。
方向感應器
AbsoluteOrientationSensor
是融合感應器,可測量裝置相對於地球座標系統的旋轉角度,而 RelativeOrientationSensor
則會提供裝置相對於靜止參考座標系統的旋轉角度資料。
所有新式 3D JavaScript 架構都支援 四元數和旋轉矩陣,用於表示旋轉;不過,如果您直接使用 WebGL,OrientationSensor
會方便地同時提供 quaternion
屬性和 populateMatrix()
方法。以下是幾個程式碼片段:
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);
方向感應器支援多種用途,例如沉浸式遊戲、擴增和虛擬實境。
如要進一步瞭解動作感應器、進階用途和相關規定,請參閱動作感應器說明文件。
與螢幕座標同步
根據預設,系統會在繫結裝置的本機座標系統中解析空間感應器的讀數,但不會將螢幕方向納入考量。
不過,許多用途 (例如遊戲或擴增和虛擬實境) 都需要在與螢幕方向綁定的座標系統中解析感應器讀數。
先前,您必須使用 JavaScript 將感應器讀數重新對應至螢幕座標。這種做法效率不彰,而且會大幅增加網頁應用程式程式碼的複雜度;網頁應用程式必須監控螢幕方向變更,並為感應器讀數執行座標轉換,而這對於歐拉角或四元數來說,並非易事。
Generic Sensor API 提供更加簡單可靠的解決方案!當地座標系統可針對所有定義的空間感應器類別進行設定:Accelerometer
、Gyroscope
、LinearAccelerationSensor
、AbsoluteOrientationSensor
、RelativeOrientationSensor
和 Magnetometer
。將 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
旋轉。
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 開發人員工具可提供模擬裝置方向的絕佳支援。
隱私權與安全性
感應器讀數屬於機密資料,可能會遭受惡意網頁的各種攻擊。通用感應器 API 的實作會強制執行一些限制,以降低可能的安全性和隱私權風險。有意使用 API 的開發人員必須考量這些限制,因此讓我們簡單列出這些限制。
僅限 HTTPS
由於 Generic Sensor API 是強大的功能,瀏覽器只能在安全的情況下使用。換句話說,如要使用 Generic Sensor API,您必須透過 HTTPS 存取網頁。在開發期間,您可以透過 http://localhost 執行這項操作,但在實際工作環境中,則需要在伺服器採用 HTTPS。如需最佳做法和指南,請參閱安全無虞系列。
權限政策整合
通用感應器 API 中的權限政策整合功能,可控管影格感應器資料的存取權。
根據預設,Sensor
物件只能在主框架或同源子框架中建立,因此可防止跨來源 iframe 未經授權就讀取感應器資料。您可以明確啟用或停用對應的政策控管功能,藉此修改這項預設行為。
下方程式碼片段說明如何授予跨來源 iframe 的加速計資料存取權,代表現在您可以在該處建立 Accelerometer
或 LinearAccelerationSensor
物件。
<iframe src="https://third-party.com" allow="accelerometer" />
可暫停傳送感應器讀數
只有可見的網頁 (也就是使用者實際與網頁互動時) 才能存取感應器讀數。此外,如果使用者焦點變更為跨來源子影格,系統就不會將感應器資料提供給父項影格。這樣可以防止上層頁框推斷使用者輸入內容。
後續步驟
在近期,我們將實作一組已指定的感應器類別,例如環境光度感應器或鄰近感應器;不過,由於通用感應器架構的可擴充性極佳,我們可以預期會有更多代表各種感應器類型的新類別出現。
未來另一個重要的工作領域,是改善通用感應器 API 本身。通用感應器規格目前是候選推薦規格,也就是說,我們仍有時間進行修正,並提供開發人員需要的新功能。
讓我們來幫忙!
感應器規格已達到候選推薦的成熟度,因此非常歡迎網頁和瀏覽器開發人員提供意見回饋。請告訴我們您認為哪些功能很適合加入,或是您想修改目前 API 中的哪些內容。
資源
- 示範專案:https://intel.github.io/generic-sensor-demos/
- 通用感應器 API 規格:https://w3c.github.io/sensors/
- 規格問題:https://github.com/w3c/sensors/issues
- W3C 工作群組郵寄清單:public-device-apis@w3.org
- Chrome 功能狀態: https://www.chromestatus.com/feature/5698781827825664
- 實作錯誤: http://crbug.com?q=component:Blink>Sensor
特別銘謝
本文由 Joe Medley 和 Kayce Basques 審查。