移動開發(fā)指南:設備定向API介紹
譯文【51CTO譯文】在現(xiàn)代移動設備當中,加速計、陀螺儀以及指南針已經成為普遍存在的標準配置。在之前討論地理定位 API的文章中,我們曾經了解過開發(fā)人員如何利用地理定位API改善用戶的使用體驗。今天我們要著重考量的則是另一種有趣的API,即設備定向API。
對于很多應用程序來說,檢測設備朝向都是實現(xiàn)自身功能的重要前提,從導航應用到體感游戲皆在此列。不知道大家有沒有玩過移動設備上的賽車游戲,我們可以將設備當作方向盤左右傾斜、從而實現(xiàn)對車輛的操控。
該API面向的另一類應用程序則關注在用戶設備朝的向發(fā)生改變時,及時調整用戶界面以充分利用屏幕空間、從而帶來更為出色的使用體驗。如果大家是YouTube的忠誠觀眾,那么肯定對這項功能的優(yōu)勢非常熟悉。
在今天的文章中,我將向大家介紹設備定向API,并解釋它能為我們提供怎樣的數(shù)據(jù)類型以及如何在自己開發(fā)的應用程序中對其加以利用。
1. 設備定位API是什么?
引用W3C中的設備定位API的規(guī)范描述可知,該API“……定義了多種新型DOM事件,旨在提供與主機設備相關的物理朝向與運動狀態(tài)信息。”由API提供的數(shù)據(jù)產生自多種來源,其中包括設備上的陀螺儀、加速計以及指南針等。不同的設備所配備的數(shù)據(jù)來源也有所區(qū)別,具體情況取決于其上搭載的傳感器類型。
該API從屬于W3C Working Draft,也就是說相關規(guī)范并非最終確定、在未來其具體內容可能還會出現(xiàn)一定程度的變動。另外值得注意的是,已知該API在多種瀏覽器以及操作系統(tǒng)之上可能出現(xiàn)不一致性。舉例來說,在基于Blink渲染引擎的Chrome與Opera瀏覽器上,該API會與Windows 8系統(tǒng)產生deviceorientation事件的兼容性沖突。另一個實例則是,該API中的 interval屬性在Opera Mobile版本中并非恒定的常數(shù)。
2. 實際使用
該API所顯示的三個事件全部用于提供與設備定位相關的信息:
• deviceorientation
• devicemotion
• compassneedscalibration
這些事件在window對象當中執(zhí)行,也就是說我們需要為window對象附加一個處理程序。下面讓我們對這三個事件進行逐一分析。
deviceorientation
首先出場的是deviceorientation事件,當加速計檢測到設備方位發(fā)生改變時,它就會被觸發(fā)。正如我之前所提到,我們可以監(jiān)聽這一事件并通過為window對象附加事件處理程序對任意變更作出回應。當事件處理程序介入時,它會獲得DeviceOrientationEvent type的一條參數(shù),其中包含以下四種屬性:
• alpha代表的是環(huán)繞z軸的角度。它的取值范圍在0到360度之間。當設備頂端指向正北方向時,該屬性的取值為0。
• beta代表的是環(huán)繞x軸的角度。它的取值范圍在-180到180度之間。當設備與地球表面保持平行時,該屬性的取值為0。
• gamma代表的是環(huán)繞y軸的角度。它的取值范圍在-90到90度之間。當該設備與地球表面保持平行時,該屬性的取值為0。
• absolute用于指定設備本身提供的定位數(shù)據(jù)是否與地球坐標系相對應。在這種情況下,大家可以將其值取為true,或者采用任意其它坐標系作為基準。
下面這幅圖片來自W3C的官方規(guī)范文件,其中標明了前面提到的相對于設備設定的x、y與z軸。
devicemotion
每當設備運動狀態(tài)出現(xiàn)加速或者減速時,devicemotio事件都會被觸發(fā)。大家可以對該事件進行監(jiān)聽,正如我們監(jiān)聽deviceorientation事件一樣。當該事件的處理程序介入時,它會獲得來自DeviceMotionEvent type的一條參數(shù),參數(shù)當中包含四種屬性:
• acceleration負責指定設備相對于地球在x、y與z軸上的加速狀況,大家可以分別通過其x、y與z屬性進行訪問。該數(shù)值的單位為m/s2。
• accelerationIncludingGravity與acceleration屬性所取的數(shù)值相同,但它會把地球重力因素考慮在內。這項屬性的取值應當被用在設備硬件不知道如何去除加速數(shù)據(jù)中重力影響效果的情況下。事實上,在這類實例當中,acceleration屬性往往不該由用戶代理提供。
• rotationRate負責指定設備在各個軸上每秒運動多少度。我們可以通過其alpha、beta與gamma屬性訪問rotationRate的各獨立取值。
• interval負責指定不同數(shù)據(jù)獲取操作之間的時間間隔。一旦設定完畢,該數(shù)值就絕不能再進行更新。它以毫秒作為計算單位。
compassneedscalibration
這個事件會在用戶代理檢測到指南針需要校準時被觸發(fā)。其規(guī)范還規(guī)定,“用戶代理應當只在校準指南針能夠增加deviceorientation事件數(shù)據(jù)準確性的前提下被觸發(fā)。”該事件應當被用于通知用戶指南針需要校準這一情況,同時需要指導用戶如何完成這一調整。
3. 檢測支持能力
檢測瀏覽器或者用戶代理是否支持前面提到的兩個事件,即deviceorientation與devicemotion,本身非常簡單,只需要添加一條微不足道的狀態(tài)聲明即可。大家可以查看以下代碼片段,我們會在其中檢測對deviceorientation事件的支持能力:
- if (window.DeviceOrientationEvent) {
- // We can listen for change in the device's orientation...
- } else {
- // Not supported
- }
為了測試compassneedscalibration事件,我們要用到以下代碼片段:
- if (!('oncompassneedscalibration' in window)) {
- // Event supported
- } else {
- // Event not supported
- }
4. 瀏覽器支持能力
即使對于設備定向API的支持能力良好,我們仍然需要考慮到其它可能與API產生沖突的重要因素。除了介紹當中提到的事項,absolute屬性在Mobile Safari還會出現(xiàn)undefined問題。
不過真正的問題在于,每一款能夠支持設備定向API的瀏覽器都只能實現(xiàn)部分支持。事實上,就在我撰寫這份文章的時候,仍然幾乎沒有幾種瀏覽器能夠支持compassneedscalibration事件。在Chrome或者火狐瀏覽器中執(zhí)行上述代碼片段時就會出現(xiàn)這類問題。
有鑒于此,能夠支持設備定位API的桌面版本瀏覽器包括Chrome 7及以上版本、火狐6及以上版本、Opera 15及以上版本再加上IE 11。這類支持能力在移動瀏覽器上表現(xiàn)得更好。除了前面提到過的瀏覽器之外,該API的支持能力還存在于BlackBerry 10、Opera Mobile 12及以上版本、Mobile Safari 4.2及以上版本外加Android上的Chrome 3及以上版本中。
對于目前對于設備定位API的準確支持能力,我建議大家點擊此處查閱細節(jié)信息。
5. 演示
現(xiàn)在我們已經明確了需要創(chuàng)建怎樣的演示應用來利用設備定位API。這套演示實例的目的在于建立一個采用普通HTML與CSS的方塊,并在設備位置發(fā)生變化時隨之進行轉動。
我們還需要檢索來自該API的信息,其中顯示我們獲取自設備定位API的數(shù)據(jù)類型。我們也會在原始文本中顯示信息,這是因為雖然一部分瀏覽器能夠支持設備定位API、但CCS屬性并不能對方塊進行渲染。舉例來講,Opera Mobile就符合這種情況。
由于已經確定了并不是每種瀏覽器都能支持該API,因此我們還需要對該API中的每一種功能進行支持能力測試并將結果傳達給用戶。
演示應用的原代碼如下所示,但大家可以點擊此處查看運行效果。
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
- <meta name="author" content="Aurelio De Rosa">
- <title>Device Orientation API Demo by Aurelio De Rosa</title>
- <style>
- *
- {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- body
- {
- max-width: 500px;
- margin: 2em auto;
- padding: 0 0.5em;
- font-size: 20px;
- }
- h1
- {
- text-align: center;
- }
- .hidden
- {
- display: none;
- }
- .cube
- {
- width: 150px;
- height: 150px;
- position: relative;
- margin: 30px auto;
- -webkit-transform-style: preserve-3d;
- transform-style: preserve-3d;
- }
- .face
- {
- width: 150px;
- height: 150px;
- position: absolute;
- font-size: 80px;
- text-align: center;
- line-height: 150px;
- background-color: #999999;
- box-shadow: inset 0 0 20px #333333;
- opacity: 0.6;
- }
- .cube .one
- {
- -webkit-transform: translateZ(75px);
- transform: translateZ(75px);
- }
- .cube .two
- {
- -webkit-transform: rotateY(90deg) translateZ(75px);
- transform: rotateY(90deg) translateZ(75px);
- }
- .cube .three
- {
- -webkit-transform: rotateY(180deg) translateZ(75px);
- transform: rotateY(180deg) translateZ(75px);
- }
- .cube .four
- {
- -webkit-transform: rotateY(-90deg) translateZ(75px);
- transform: rotateY(-90deg) translateZ(75px);
- }
- .cube .five
- {
- -webkit-transform: rotateX(90deg) translateZ(75px);
- transform: rotateX(90deg) translateZ(75px);
- }
- .cube .six
- {
- -webkit-transform: rotateX(-90deg) translateZ(75px) rotate(0deg);
- transform: rotateX(-90deg) translateZ(75px) rotate(0deg);
- }
- .value
- {
- font-weight: bold;
- }
- .author
- {
- display: block;
- margin-top: 1em;
- }
- </style>
- </head>
- <body>
- <h1>Device Orientation API</h1>
- <span id="do-unsupported" class="hidden">deviceorientation event not supported</span>
- <span id="dm-unsupported" class="hidden">devicemotion event not supported</span>
- <span id="cnc-unsupported" class="hidden">compassneedscalibration event not supported</span>
- <div id="do-results">
- <div id="cube" class="cube">
- <div class="face one">1</div>
- <div class="face two">2</div>
- <div class="face three">3</div>
- <div class="face four">4</div>
- <div class="face five">5</div>
- <div class="face six">6</div>
- </div>
- <div id="do-info" class="hidden">
- <p>
- Coordinates:
- (<span id="beta" class="value">null</span>,
- <span id="gamma" class="value">null</span>,
- <span id="alpha" class="value">null</span>)
- </p>
- Position absolute? <span id="is-absolute" class="value">unavailable</span>
- </p>
- </div>
- <div id="dm-info" class="hidden">
- <p>
- Acceleration:
- (<span id="acceleration-x" class="value">null</span>,
- <span id="acceleration-y" class="value">null</span>,
- <span id="acceleration-z" class="value">null</span>)
- m/s<sup>2</sup>
- </p>
- <p>
- Acceleration including gravity:
- (<span id="acceleration-including-gravity-x" class="value">null</span>,
- <span id="acceleration-including-gravity-y" class="value">null</span>,
- <span id="acceleration-including-gravity-z" class="value">null</span>)
- m/s<sup>2</sup>
- </p>
- <p>
- Rotation rate:
- (<span id="rotation-rate-beta" class="value">null</span>,
- <span id="rotation-rate-gamma" class="value">null</span>,
- <span id="rotation-rate-alpha" class="value">null</span>)
- </p>
- <p>
- Interval: <span id="interval" class="value">0</span> milliseconds
- </p>
- </div>
- </div>
- <small class="author">
- Demo created by <a href="http://www.audero.it">Aurelio De Rosa</a>
- (<a href="https://twitter.com/AurelioDeRosa">@AurelioDeRosa</a>)
- </small>
- <script>
- if (!window.DeviceOrientationEvent) {
- document.getElementById('do-unsupported').classList.remove('hidden');
- } else {
- document.getElementById('do-info').classList.remove('hidden');
- window.addEventListener('deviceorientation', function(event) {
- document.getElementById('cube').style.webkitTransform =
- document.getElementById('cube').style.transform =
- 'rotateX(' + event.beta + 'deg) ' +
- 'rotateY(' + event.gamma + 'deg) ' +
- 'rotateZ(' + event.alpha + 'deg)';
- document.getElementById('beta').innerHTML = Math.round(event.beta);
- document.getElementById('gamma').innerHTML = Math.round(event.gamma);
- document.getElementById('alpha').innerHTML = Math.round(event.alpha);
- document.getElementById('is-absolute').innerHTML = event.absolute ? "true" : "false";
- });
- }
- if (!window.DeviceMotionEvent) {
- document.getElementById('dm-unsupported').classList.remove('hidden');
- } else {
- document.getElementById('dm-info').classList.remove('hidden');
- window.addEventListener('devicemotion', function(event) {
- document.getElementById('acceleration-x').innerHTML = Math.round(event.acceleration.x);
- document.getElementById('acceleration-y').innerHTML = Math.round(event.acceleration.y);
- document.getElementById('acceleration-z').innerHTML = Math.round(event.acceleration.z);
- document.getElementById('acceleration-including-gravity-x').innerHTML =
- Math.round(event.accelerationIncludingGravity.x);
- document.getElementById('acceleration-including-gravity-y').innerHTML =
- Math.round(event.accelerationIncludingGravity.y);
- document.getElementById('acceleration-including-gravity-z').innerHTML =
- Math.round(event.accelerationIncludingGravity.z);
- document.getElementById('rotation-rate-beta').innerHTML = Math.round(event.rotationRate.beta);
- document.getElementById('rotation-rate-gamma').innerHTML = Math.round(event.rotationRate.gamma);
- document.getElementById('rotation-rate-alpha').innerHTML = Math.round(event.rotationRate.alpha);
- document.getElementById('interval').innerHTML = event.interval;
- });
- }
- if (!('oncompassneedscalibration' in window)) {
- document.getElementById('cnc-unsupported').classList.remove('hidden');
- } else {
- window.addEventListener('compassneedscalibration', function(event) {
- alert('Compass needs calibrating! Wave your device in a figure-eight motion');
- });
- }
- </script>
- </body>
- </html>
總結
在今天的文章中,我們已經通過了解設備定位API的特性與潛在作用建立了對其的初步認識。在撰寫本文的時候,對于該API的支持還比較有限,不過我可以肯定它的出現(xiàn)為移動應用開發(fā)者、特別是游戲開發(fā)者帶來了無窮的可能性。再次建議大家點擊此處查看該API的實際演示運行效果。
原文:An Introduction to the Device-Orientation API
核子可樂譯