移動(dòng)開(kāi)發(fā)指南:設(shè)備定向API介紹
譯文【51CTO譯文】在現(xiàn)代移動(dòng)設(shè)備當(dāng)中,加速計(jì)、陀螺儀以及指南針已經(jīng)成為普遍存在的標(biāo)準(zhǔn)配置。在之前討論地理定位 API的文章中,我們?cè)?jīng)了解過(guò)開(kāi)發(fā)人員如何利用地理定位API改善用戶(hù)的使用體驗(yàn)。今天我們要著重考量的則是另一種有趣的API,即設(shè)備定向API。
對(duì)于很多應(yīng)用程序來(lái)說(shuō),檢測(cè)設(shè)備朝向都是實(shí)現(xiàn)自身功能的重要前提,從導(dǎo)航應(yīng)用到體感游戲皆在此列。不知道大家有沒(méi)有玩過(guò)移動(dòng)設(shè)備上的賽車(chē)游戲,我們可以將設(shè)備當(dāng)作方向盤(pán)左右傾斜、從而實(shí)現(xiàn)對(duì)車(chē)輛的操控。
該API面向的另一類(lèi)應(yīng)用程序則關(guān)注在用戶(hù)設(shè)備朝的向發(fā)生改變時(shí),及時(shí)調(diào)整用戶(hù)界面以充分利用屏幕空間、從而帶來(lái)更為出色的使用體驗(yàn)。如果大家是YouTube的忠誠(chéng)觀眾,那么肯定對(duì)這項(xiàng)功能的優(yōu)勢(shì)非常熟悉。
在今天的文章中,我將向大家介紹設(shè)備定向API,并解釋它能為我們提供怎樣的數(shù)據(jù)類(lèi)型以及如何在自己開(kāi)發(fā)的應(yīng)用程序中對(duì)其加以利用。
1. 設(shè)備定位API是什么?
引用W3C中的設(shè)備定位API的規(guī)范描述可知,該API“……定義了多種新型DOM事件,旨在提供與主機(jī)設(shè)備相關(guān)的物理朝向與運(yùn)動(dòng)狀態(tài)信息。”由API提供的數(shù)據(jù)產(chǎn)生自多種來(lái)源,其中包括設(shè)備上的陀螺儀、加速計(jì)以及指南針等。不同的設(shè)備所配備的數(shù)據(jù)來(lái)源也有所區(qū)別,具體情況取決于其上搭載的傳感器類(lèi)型。
該API從屬于W3C Working Draft,也就是說(shuō)相關(guān)規(guī)范并非最終確定、在未來(lái)其具體內(nèi)容可能還會(huì)出現(xiàn)一定程度的變動(dòng)。另外值得注意的是,已知該API在多種瀏覽器以及操作系統(tǒng)之上可能出現(xiàn)不一致性。舉例來(lái)說(shuō),在基于Blink渲染引擎的Chrome與Opera瀏覽器上,該API會(huì)與Windows 8系統(tǒng)產(chǎn)生deviceorientation事件的兼容性沖突。另一個(gè)實(shí)例則是,該API中的 interval屬性在Opera Mobile版本中并非恒定的常數(shù)。
2. 實(shí)際使用
該API所顯示的三個(gè)事件全部用于提供與設(shè)備定位相關(guān)的信息:
• deviceorientation
• devicemotion
• compassneedscalibration
這些事件在window對(duì)象當(dāng)中執(zhí)行,也就是說(shuō)我們需要為window對(duì)象附加一個(gè)處理程序。下面讓我們對(duì)這三個(gè)事件進(jìn)行逐一分析。
deviceorientation
首先出場(chǎng)的是deviceorientation事件,當(dāng)加速計(jì)檢測(cè)到設(shè)備方位發(fā)生改變時(shí),它就會(huì)被觸發(fā)。正如我之前所提到,我們可以監(jiān)聽(tīng)這一事件并通過(guò)為window對(duì)象附加事件處理程序?qū)θ我庾兏鞒龌貞?yīng)。當(dāng)事件處理程序介入時(shí),它會(huì)獲得DeviceOrientationEvent type的一條參數(shù),其中包含以下四種屬性:
• alpha代表的是環(huán)繞z軸的角度。它的取值范圍在0到360度之間。當(dāng)設(shè)備頂端指向正北方向時(shí),該屬性的取值為0。
• beta代表的是環(huán)繞x軸的角度。它的取值范圍在-180到180度之間。當(dāng)設(shè)備與地球表面保持平行時(shí),該屬性的取值為0。
• gamma代表的是環(huán)繞y軸的角度。它的取值范圍在-90到90度之間。當(dāng)該設(shè)備與地球表面保持平行時(shí),該屬性的取值為0。
• absolute用于指定設(shè)備本身提供的定位數(shù)據(jù)是否與地球坐標(biāo)系相對(duì)應(yīng)。在這種情況下,大家可以將其值取為true,或者采用任意其它坐標(biāo)系作為基準(zhǔn)。
下面這幅圖片來(lái)自W3C的官方規(guī)范文件,其中標(biāo)明了前面提到的相對(duì)于設(shè)備設(shè)定的x、y與z軸。
devicemotion
每當(dāng)設(shè)備運(yùn)動(dòng)狀態(tài)出現(xiàn)加速或者減速時(shí),devicemotio事件都會(huì)被觸發(fā)。大家可以對(duì)該事件進(jìn)行監(jiān)聽(tīng),正如我們監(jiān)聽(tīng)deviceorientation事件一樣。當(dāng)該事件的處理程序介入時(shí),它會(huì)獲得來(lái)自DeviceMotionEvent type的一條參數(shù),參數(shù)當(dāng)中包含四種屬性:
• acceleration負(fù)責(zé)指定設(shè)備相對(duì)于地球在x、y與z軸上的加速狀況,大家可以分別通過(guò)其x、y與z屬性進(jìn)行訪問(wèn)。該數(shù)值的單位為m/s2。
• accelerationIncludingGravity與acceleration屬性所取的數(shù)值相同,但它會(huì)把地球重力因素考慮在內(nèi)。這項(xiàng)屬性的取值應(yīng)當(dāng)被用在設(shè)備硬件不知道如何去除加速數(shù)據(jù)中重力影響效果的情況下。事實(shí)上,在這類(lèi)實(shí)例當(dāng)中,acceleration屬性往往不該由用戶(hù)代理提供。
• rotationRate負(fù)責(zé)指定設(shè)備在各個(gè)軸上每秒運(yùn)動(dòng)多少度。我們可以通過(guò)其alpha、beta與gamma屬性訪問(wèn)rotationRate的各獨(dú)立取值。
• interval負(fù)責(zé)指定不同數(shù)據(jù)獲取操作之間的時(shí)間間隔。一旦設(shè)定完畢,該數(shù)值就絕不能再進(jìn)行更新。它以毫秒作為計(jì)算單位。
compassneedscalibration
這個(gè)事件會(huì)在用戶(hù)代理檢測(cè)到指南針需要校準(zhǔn)時(shí)被觸發(fā)。其規(guī)范還規(guī)定,“用戶(hù)代理應(yīng)當(dāng)只在校準(zhǔn)指南針能夠增加deviceorientation事件數(shù)據(jù)準(zhǔn)確性的前提下被觸發(fā)。”該事件應(yīng)當(dāng)被用于通知用戶(hù)指南針需要校準(zhǔn)這一情況,同時(shí)需要指導(dǎo)用戶(hù)如何完成這一調(diào)整。
3. 檢測(cè)支持能力
檢測(cè)瀏覽器或者用戶(hù)代理是否支持前面提到的兩個(gè)事件,即deviceorientation與devicemotion,本身非常簡(jiǎn)單,只需要添加一條微不足道的狀態(tài)聲明即可。大家可以查看以下代碼片段,我們會(huì)在其中檢測(cè)對(duì)deviceorientation事件的支持能力:
- if (window.DeviceOrientationEvent) {
- // We can listen for change in the device's orientation...
- } else {
- // Not supported
- }
為了測(cè)試compassneedscalibration事件,我們要用到以下代碼片段:
- if (!('oncompassneedscalibration' in window)) {
- // Event supported
- } else {
- // Event not supported
- }
4. 瀏覽器支持能力
即使對(duì)于設(shè)備定向API的支持能力良好,我們?nèi)匀恍枰紤]到其它可能與API產(chǎn)生沖突的重要因素。除了介紹當(dāng)中提到的事項(xiàng),absolute屬性在Mobile Safari還會(huì)出現(xiàn)undefined問(wèn)題。
不過(guò)真正的問(wèn)題在于,每一款能夠支持設(shè)備定向API的瀏覽器都只能實(shí)現(xiàn)部分支持。事實(shí)上,就在我撰寫(xiě)這份文章的時(shí)候,仍然幾乎沒(méi)有幾種瀏覽器能夠支持compassneedscalibration事件。在Chrome或者火狐瀏覽器中執(zhí)行上述代碼片段時(shí)就會(huì)出現(xiàn)這類(lèi)問(wèn)題。
有鑒于此,能夠支持設(shè)備定位API的桌面版本瀏覽器包括Chrome 7及以上版本、火狐6及以上版本、Opera 15及以上版本再加上IE 11。這類(lèi)支持能力在移動(dòng)瀏覽器上表現(xiàn)得更好。除了前面提到過(guò)的瀏覽器之外,該API的支持能力還存在于BlackBerry 10、Opera Mobile 12及以上版本、Mobile Safari 4.2及以上版本外加Android上的Chrome 3及以上版本中。
對(duì)于目前對(duì)于設(shè)備定位API的準(zhǔn)確支持能力,我建議大家點(diǎn)擊此處查閱細(xì)節(jié)信息。
5. 演示
現(xiàn)在我們已經(jīng)明確了需要?jiǎng)?chuàng)建怎樣的演示應(yīng)用來(lái)利用設(shè)備定位API。這套演示實(shí)例的目的在于建立一個(gè)采用普通HTML與CSS的方塊,并在設(shè)備位置發(fā)生變化時(shí)隨之進(jìn)行轉(zhuǎn)動(dòng)。
我們還需要檢索來(lái)自該API的信息,其中顯示我們獲取自設(shè)備定位API的數(shù)據(jù)類(lèi)型。我們也會(huì)在原始文本中顯示信息,這是因?yàn)殡m然一部分瀏覽器能夠支持設(shè)備定位API、但CCS屬性并不能對(duì)方塊進(jìn)行渲染。舉例來(lái)講,Opera Mobile就符合這種情況。
由于已經(jīng)確定了并不是每種瀏覽器都能支持該API,因此我們還需要對(duì)該API中的每一種功能進(jìn)行支持能力測(cè)試并將結(jié)果傳達(dá)給用戶(hù)。
演示應(yīng)用的原代碼如下所示,但大家可以點(diǎn)擊此處查看運(yùn)行效果。
- <!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>
總結(jié)
在今天的文章中,我們已經(jīng)通過(guò)了解設(shè)備定位API的特性與潛在作用建立了對(duì)其的初步認(rèn)識(shí)。在撰寫(xiě)本文的時(shí)候,對(duì)于該API的支持還比較有限,不過(guò)我可以肯定它的出現(xiàn)為移動(dòng)應(yīng)用開(kāi)發(fā)者、特別是游戲開(kāi)發(fā)者帶來(lái)了無(wú)窮的可能性。再次建議大家點(diǎn)擊此處查看該API的實(shí)際演示運(yùn)行效果。
原文:An Introduction to the Device-Orientation API
核子可樂(lè)譯