Статьи

Введение в API ориентации устройства

Большинство современных мобильных устройств оснащены акселерометрами, гироскопами и компасами. В моей предыдущей статье об API геолокации я описал, как разработчики могут использовать данные, предоставляемые API геолокации, для улучшения взаимодействия с пользователем. Другой интересный API-интерфейс — это Device Orientation API, на котором сосредоточено внимание в этом руководстве.

Определение ориентации устройства полезно для широкого спектра приложений, от навигационных приложений до игр. Вы когда-нибудь играли в гоночную игру на мобильном устройстве, которое позволяет вам управлять автомобилем, наклоняя устройство?

Другое приложение API обновляет пользовательский интерфейс приложения, когда ориентация устройства изменяется, чтобы предложить пользователю наилучший возможный опыт, используя весь экран. Если вы поклонник YouTube, то вы наверняка воспользовались этой возможностью.

В этой статье я познакомлю вас с Device Orientation API, объясню, какой тип данных он может нам предложить и как использовать его в ваших приложениях.

Чтобы процитировать спецификацию W3C API -интерфейса устройства, API «[…] определяет несколько новых событий DOM, которые предоставляют информацию о физической ориентации и движении хост-устройства». Данные, предоставленные API, получены из различных источников, таких как гироскоп устройства, акселерометр и компас. Это отличается от устройства к устройству, в зависимости от того, какие датчики доступны.

Этот API является рабочим проектом W3C, что означает, что спецификация нестабильна, и мы можем ожидать некоторые изменения в будущем. Стоит также отметить, что этот API имеет некоторые известные несоответствия в нескольких браузерах и в ряде операционных систем. Например, реализация в Chrome и Opera, основанная на механизме рендеринга Blink, имеет проблему совместимости с Windows 8 для события deviceorientation . Другим примером является то, что свойство interval не является постоянным в Opera Mobile.

API предоставляет три события, которые предоставляют информацию об ориентации устройства:

  • deviceorientation
  • devicemotion
  • compassneedscalibration

Эти события запускаются на объекте window , что означает, что нам нужно прикрепить обработчик к объекту window . Давайте посмотрим на каждое из этих событий.

Событие ориентации устройства вызывается, когда акселерометр обнаруживает изменение ориентации устройства. Как я упоминал ранее, мы можем прослушивать это событие и реагировать на любые изменения, прикрепляя обработчик событий к объекту window . Когда обработчик события вызывается, он получает один аргумент типа DeviceOrientationEvent , который содержит четыре свойства:

  • alpha это угол вокруг оси Z. Его значение колеблется от 0 до 360 градусов. Когда верхняя часть устройства указывает на истинный север , значение этого свойства равно 0 .
  • beta угол вокруг оси х. Диапазон его значений от -180 до 180 градусов. Когда устройство параллельно поверхности Земли, значение этого свойства равно 0 .
  • gamma — это угол вокруг оси Y. Его значения колеблются от -90 до 90 градусов. Когда устройство расположено параллельно поверхности Земли, значение этого свойства равно 0 .
  • absolute указывает, предоставляет ли устройство данные об ориентации, относящиеся к системе координат Земли, в этом случае его значение равно true , или к произвольной системе координат.

На следующем рисунке, взятом из официальной спецификации , показаны оси x, y и z, упомянутые относительно устройства.

Изображение, показывающее устройство и оси x y и z

Событие devicemotion устройства запускается каждый раз, когда устройство ускоряется или замедляется. Вы можете прослушать это событие так же, как мы это делали для события deviceorientation . Когда вызывается обработчик события, он получает один аргумент типа DeviceMotionEvent , который имеет четыре свойства:

  • acceleration определяет ускорение устройства относительно системы координат Земли по осям x, y и z, доступное через его свойства x , y и z . Значения выражены в м / с 2 .
  • accelerationIncludingGravity содержит те же значения, что и свойство acceleration , но учитывает гравитацию Земли. Значения этого свойства следует использовать в ситуациях, когда аппаратное обеспечение устройства не знает, как удалить гравитацию из данных ускорения. Фактически, в таких случаях свойство acceleration не должно предоставляться пользовательским агентом.
  • rotationRate указывает скорость, с которой устройство вращается вокруг каждой своей оси в градусах в секунду. Мы можем получить доступ к отдельным значениям rotationRate помощью его свойств alpha , beta и gamma .
  • interval обеспечивает интервал, в котором данные получены. Это значение не должно изменяться после его установки. Выражается в миллисекундах.

Это событие вызывается, когда пользовательский агент определяет, что компас требует калибровки. В спецификации также говорится, что «пользовательские агенты должны deviceorientation событие только в том случае, если калибровка компаса повысит точность данных, предоставляемых событием deviceorientation ». Это событие должно использоваться для информирования пользователя о том, что компас нуждается в калибровке, и в нем также должно быть указано, как это сделать.

Чтобы определить, поддерживает ли браузер или пользовательский агент одно из первых двух событий, deviceorientation и devicemotion , достаточно просто включить тривиальный условный оператор. Взгляните на следующий фрагмент кода, в котором мы обнаруживаем поддержку события deviceorientation :

1
2
3
4
5
if (window.DeviceOrientationEvent) {
   // We can listen for change in the device’s orientation…
} else {
   // Not supported
}

Чтобы проверить событие compassneedscalibration , мы используем следующий фрагмент кода:

1
2
3
4
5
if (!(‘oncompassneedscalibration’ in window)) {
   // Event supported
} else {
   // Event not supported
}

Несмотря на то, что поддержка Device Orientation API хорошая, нам нужно помнить несколько вещей при работе с API. Помимо предостережений, упомянутых во введении, absolute свойство не undefined в Mobile Safari.

Однако реальная проблема заключается в том, что каждый браузер, который поддерживает Device Orientation API, поддерживает его только частично. На самом деле, на момент написания очень немногие браузеры поддерживали событие compassneedscalibration . Выполните приведенный выше фрагмент кода в Chrome или Firefox, чтобы проиллюстрировать проблему.

Учитывая это, браузерами, поддерживающими Device Orientation API, являются Chrome 7+, Firefox 6+, Opera 15+ и Internet Explorer 11. Поддержка мобильных браузеров еще лучше. В дополнение к уже упомянутым, API также поддерживается браузером BlackBerry 10, Opera Mobile 12+, Mobile Safari 4.2+ и Chrome 3+ на Android.

Чтобы получить актуальную и точную картину поддержки API-интерфейса устройства, я рекомендую посетить страницу « Можно ли использовать …» .

Теперь мы знаем, что нам нужно для создания демонстрационного приложения, использующего API-интерфейс устройства. Цель этой демонстрации — создать куб, используя обычный HTML и CSS, и вращать его по мере изменения ориентации устройства.

Мы также отобразим информацию, которую мы получаем из API, которая показывает тип данных, которые мы получаем из API ориентации устройства. Мы также показываем информацию в виде необработанного текста, поскольку некоторые браузеры могут поддерживать API-интерфейс устройства, но не свойства CSS для визуализации куба. Это относится к Opera Mobile, например.

Поскольку мы знаем, что не каждый браузер поддерживает API, мы также тестируем поддержку каждой функции API и показываем это пользователю.

Исходный код демонстрационного приложения показан ниже, но вы также можете увидеть его в действии .

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<!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 id=»dm-unsupported» class=»hidden»>devicemotion event not supported
      <span id=»cnc-unsupported» class=»hidden»>compassneedscalibration event not supported
 
      <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 id=»gamma» class=»value»>null
               <span id=»alpha» class=»value»>null
               <br />
               Position absolute?
            </p>
         </div>
         <div id=»dm-info» class=»hidden»>
            <p>
               Acceleration:
               (<span id=»acceleration-x» class=»value»>null
               <span id=»acceleration-y» class=»value»>null
               <span id=»acceleration-z» class=»value»>null
               m/s<sup>2</sup>
            </p>
            <p>
               Acceleration including gravity:
               (<span id=»acceleration-including-gravity-x» class=»value»>null
               <span id=»acceleration-including-gravity-y» class=»value»>null
               <span id=»acceleration-including-gravity-z» class=»value»>null
               m/s<sup>2</sup>
            </p>
            <p>
               Rotation rate:
               (<span id=»rotation-rate-beta» class=»value»>null
               <span id=»rotation-rate-gamma» class=»value»>null
               <span id=»rotation-rate-alpha» class=»value»>null
            </p>
            <p>
               Interval: <span id=»interval» class=»value»>0
            </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 ?
            });
         }
 
         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 не очень хороша, но я уверен, что вы согласны с тем, что она открывает множество возможностей для разработчиков мобильных приложений, в частности разработчиков игр. Не забудьте поиграть с демо, чтобы увидеть API в действии.