Friday, April 8, 2016

HTML5 -The Orientation API

The Orientation API

INTRODUCTION

This section covers the HTML5 orientation API: a way to use angle measures provided by accelerometers from mobile devices or laptops such as MacBooks.
Beware: all examples must be run on a device with an orientation sensor and/or with an accelerometer. Furthermore, Chrome/Safari and Mozilla support these API, but Opera mobile doesn't yet at the time of writing. 
You can "fake the orientation values" using the devtools of a desktop browser, if it provides a "mobile device emulation mode" (see the support table below, the columns for desktop versions of browsers are about the support for this emulation mode).
Here is a table that shows support for this API as of December 2015:
Orientation API support
For an up to date version of this table: http://caniuse.com/#feat=deviceorientation
Notice that all major browsers support the API in their mobile version.

EXTERNAL RESOURCES:

    • Article on html5Rocks.com about device orientation

      The coordinate system and Euler angles

      The transformation from the Earth coordinate frame to the device coordinate frame uses the following system of rotations.
      Rotations use the right-hand convention, such that positive rotation around an axis is clockwise when viewed along the positive direction of the axis. Starting with the two frames aligned, the rotations are applied in the following order:

      FIRST EXAMPLE: ROTATE THE DEVICE FRAME AROUND ITS Z AXIS BY ALPHA DEGREES, WITH ALPHA IN [0, 360]

      Device in the initial position, with Earth (XYZ) and
      body (xyz) frames aligned.
      start orientation
      Device rotated through angle alpha about z axis,
      with previous locations of x and y axes shown as x0and y0.
      rotation about z axis


      SECOND EXAMPLE: ROTATE THE DEVICE FRAME AROUND ITS X AXIS BY BETADEGREES, WITH BETA IN [-180, 180]

      Device in the initial position, with Earth (XYZ) and 
      body (xyz) frames aligned.
      start orientation
      Device rotated through angle beta about new x axis, 
      with previous locations of y and z axes shown as y0and z0.
      rotation about x axis

      THIRD EXAMPLE: ROTATE THE DEVICE FRAME AROUND ITS Y AXIS BY GAMMA DEGREES, WITH GAMMA IN [-90, 90]

      Device in the initial position, with Earth (XYZ) and 
      body (xyz) frames aligned.
      start orientation
      Device rotated through angle gamma about new y axis,
      with previous locations of x and z axes shown as x0 and z0.
      rotation about y axis

    • Get the different angles using the JavaScript HTML5 orientation API

      TYPICAL USE

      The use of this API is very straightforward:
        1. Test if your browser supports the orientation API (test if window.DeviceOrientationEvent is not null),
        2. Define a listener for the 'deviceorientation' event as follows:window.addEventListener('deviceorientation', callback); with the callback function accepting the event as a single input parameter,
        3. Get the angles from the event (use its properties alphabetagamma).
      Let's see this with an example on JsBin. Try it with  a smartphone, a tablet or a device with an accelerometer: 
      (If using a mobile device, it's better to open this URL in standalone mode, without the JsBin editor. Try this URL instead!
      orientation api 2
      The above screenshot was taken with an ipad lying immobile on a desk. All the angle values are theoretically 0 when the device is laid flat, and it has not changed since the page loaded. Depending on the hardware, however, these values may change even if the device is stationary: a little sensitive sensor can report constantly changing values. This is why in the example we round the returned values with Math.round() at display time (see code).
      If we change the orientation of the device here are the results:
      orientation api 3orientation api 1orientation api 2
      Typical use / code from the above example:
      1. ...
      2. <h2>Device Orientation with HTML5</h2>
      3. You need to be on a mobile device or use a laptop with accelerometer/orientation
      4. device.
      5. <p>
      6. <div id="LR"></div>
      7. <div id="FB"></div>
      8. <div id="DIR"></div>
      9. <script type="text/javascript">
      10.    if (window.DeviceOrientationEvent) {
      11.       console.log("DeviceOrientation is supported");
      12.       window.addEventListener('deviceorientation', function(eventData) {
      13.          // gamme is for left/right inclination
      14.          var LR = eventData.gamma;
      15.          // beta is for front/back inclination
      16.          var FB = eventData.beta;
      17.          // alpha is for orientation
      18.          var DIR = eventData.alpha;
      19.          // display values on screen
      20.          deviceOrientationHandler(LR, FB, DIR);
      21.       }, false);
      22.    } else {
      23.       alert("Device orientation not supported on your device or browser. Sorry.");
      24.    }
      25. function deviceOrientationHandler(LR, FB, DIR) {
      26.    document.querySelector("#LR").innerHTML = "gamma : " + Math.round(LR);
      27.    document.querySelector("#FB").innerHTML = "beta : " + Math.round(FB);
      28.    document.querySelector("#DIR").innerHTML = "alpha : " + Math.round(DIR);
      29. }
      30. </script>
      31.  ...

      ANOTHER EXAMPLE THAT SHOWS HOW TO ORIENT THE HTML5 LOGO USING THE ORIENTATION API + CSS3 3D ROTATIONS

      This is just a variation of the previous example, try it at JsBin:  if using a mobile device it's better to open it in standalone mode, use this URL instead
      orientation api example 3
      Results on the ipad: the logo rotates when we change the ipad's orientation. This is a good "visual feedback" for an orientation controlled game...
      logo 1logo 2logo 3
      This example as a Youtube video: http://www.youtube.com/watch?v=OrNLhOAGSdE
      Code from the example:
      1. ...
      2. <h2>Device Orientation with HTML5</h2>
      3. You need to be on a mobile device or use a laptop with accelerometer/orientation
      4. device.
      5. <p>
      6. <div id="LR"></div>
      7. <div id="FB"></div>
      8. <div id="DIR"></div>
      9. <img src="http://www.html5
      10. rocks.com/en/tutorials/device/orientation/html5_logo.png" id="imgLogo"
      11. class="logo">
      12. <script type="text/javascript">
      13.    if (window.DeviceOrientationEvent) {
      14.       console.log("DeviceOrientation is supported");
      15.       window.addEventListener('deviceorientation', function(eventData) {
      16.           var LR = eventData.gamma;
      17.           var FB = eventData.beta;
      18.           var DIR = eventData.alpha;
      19.           deviceOrientationHandler(LR, FB, DIR);
      20.       }, false);
      21.    } else {
      22.       alert("Not supported on your device or browser. Sorry.");
      23.    }
      24.    function deviceOrientationHandler(LR, FB, DIR) {
      25.       // USE CSS3 rotations for rotating the HTML5 logo
      26.       //for webkit browser
      27.       document.getElementById("imgLogo").style.webkitTransform =
      28.       "rotate(" + LR + "deg) rotate3d(1,0,0, " + (FB * -1) + "deg)";
      29.       //for HTML5 standard-compliance
      30.       document.getElementById("imgLogo").style.transform =
      31.       "rotate(" + LR + "deg) rotate3d(1,0,0, " + (FB * -1) + "deg)";
      32.       document.querySelector("#LR").innerHTML = "gamma : " + Math.round(LR);
      33.       document.querySelector("#FB").innerHTML = "beta : " + Math.round(FB);
      34.       document.querySelector("#DIR").innerHTML = "alpha : " + Math.round(DIR);
      35. }
      36. </script>
      37.  ...

      A SIMPLE LEVEL TOOL USING DEVICE ORIENTATION

      This example works in FF, Chrome and IOS Safari. Created by Derek Anderson @ Media Upstream. Original source code available GitHub.
      level tool using device orientation

      OTHER INTERESTING USES: MIX ORIENTATION API AND WEBSOCKETS

      You can imagine the above example that sends the current orientation of the device to a server using WebSockets. The server in turn updates the logo and position on a PC screen. If multiple devices connect, they can chat together and take control of the 3D Logo.
      This video shows one of the above examples slightly modified: the JavaScript code running in the Web page on the iPad sends in real time the device orientation using the Web Sockets API to a server that in turns sends the orientation to a client running on a desktop browser. In this way the tablet "controls" the HTML5 logo that is shown on the desktop browser:
      Click on the image to see the YouTube video:
      orientation API + websockets
  • The Device Motion API

    INTRODUCTION

    This section presents an API rather similar in its use to the device orientation API from the previous chapters.
    The deviceMotion API deals with accelerations instead of just orientations.
    Use cases proposed by the specification are:
      • Controlling a game: a gaming Web application monitors the device's orientation and interprets tilting in a certain direction as a means to control an on-screen sprite.
      • Gesture recognition: a Web application monitors the device's acceleration and applies signal processing in order to recognize certain specific gestures. For example, using a shaking gesture to clear a web form.
      • Mapping: a mapping Web application uses the device's orientation to correctly align the map with reality.

    BASIC USAGE

    1. function handleMotionEvent(event) {
    2.    var x = event.accelerationIncludingGravity.x;
    3.    var y = event.accelerationIncludingGravity.y;
    4.    var z = event.accelerationIncludingGravity.z;
    5.    // Process ...
    6. }
    7. window.addEventListener("devicemotion", handleMotionEvent, true);
    The deviceMotion API is rather straightforward and is very similar to the orientation API except that it returns more than just the rotation information, it also returns acceleration information about the device current motion. The acceleration is in three parts: acceleration along the X axis, the Y axis and the Z axis. Each value is in meters per second squared (m/s^2). The acceleration is returned as an "acceleration event" that has two properties: accelerationIncludingGravity and acceleration, which excludes the effects of gravity. There are two different values because some devices might be able to exclude the effect of gravity if equipped with a gyroscope. Indeed there is acceleration due implicitly to gravity, see also this: Acceleration of Gravity on Earth...
    So if the device doesn't have a gyroscope, the acceleration property will be null. In this case you have no choice but to use the accelerationIncludingGravity property. Note that all IOS devices so far have a gyroscope.

    BASICS ABOUT ACCELERATION

    The device motion event is a superset of the device orientation event; it returns data about the rotation information and also acceleration information about the device. The acceleration data is returned in three axes: x, y and z. These are measured in meters per second squared (m/s^2). Because some devices might not have the hardware to exclude the effect of gravity, the event returns two properties,accelerationIncludingGravity and acceleration. The latter excludes the effects of gravity (when this is the case, the acceleration data will be null).

    Example of acceleration values

    If a laptop is in its normal position with the screen facing up, the data returned would be (info taken from:http://www.html5rocks.com/en/tutorials/device/orientation):
    acceleration values 1
    A mobile phone rotated along the x-axis so the screen is perpendicular to its normal position would return:
    acceleration values 2
    Remember the coordinate system for a mobile phone:
    telephone coordinates system

    Common steps

    The principle of use is the same as for the orientation API:
      1. Test if the API is supported by the browser,
      2. Add a listener for 'devicemotion' events,
      3. Get the acceleration values from the DOM event that has been passed to the listener,
      4. Process the data.

    Common processing with acceleration values

    Test the value of the acceleration.z property: If > 0 then the device is facing up, otherwise it is facing down. This can be useful if you want to play heads or tails with your phone ;-)
    1. // For example, if acceleration.z is > 0 then the phone is facing up
    2. var facingUp = -1;
    3. if (acceleration.> 0) {
    4.    facingUp = +1;
    5. }
    Compute the angle corresponding to the Left / Right and Front / Back tilts. This example comes from:http://www.html5rocks.com/en/tutorials/device/orientation and uses the accelerationIncludingGravityproperty of the event.
    1. function deviceMotionHandler(eventData) {
    2.    // Grab the acceleration including gravity from the results
    3.    var acceleration = eventData.accelerationIncludingGravity;
    4.    // Convert the value from acceleration to degrees
    5.    // acceleration.x|y is the acceleration according
    6.    //  to gravity, we'll assume we're on  Earth and divide
    7.    // by 9.81 (earth gravity) to get a percentage value, 
    8.    // and then multiply that by 90 to convert to degrees.
    9.    var tiltLR = Math.round(((acceleration.x) / 9.81) * -90);
    10.    var tiltFB = Math.round(((acceleration.+ 9.81) / 9.81) * 90 * facingUp);
    11.    // ... do something
    12. }
    Compute the vertical (direction of the sky) (this extract comes from a complete example further down this page)...
    1. ...
    2. var angle = Math.atan2(accel.y,accel.x);
    3. var canvas = document.getElementById('myCanvas');
    4. var ctx = canvas.getContext('2d');
    5.  
    6. ctx.moveTo(50,50);
    7. // Draw sky direction in the canvas
    8. ctx.lineTo(50-50*Math.cos(angle),50+50*Math.sin(angle));
    9. ctx.stroke();
    Use acceleration values to move a ball on the screen of a tablet when the tablet is tilted front / back or left / right (complete example later on)...
    1. ...
    2. ball.+= acceleration.x;
    3. ball.+= acceleration.y;
    4. ...

    COMPLETE EXAMPLES

    Move the HTML5 logo

    Online example at JsBin, if using a mobile device, use this URL.
    Devicemotion API example
    Code from this example:
    1. <!doctype html>
    2. <html>
    3.    <head></head>
    4.    <body>
    5.       <h2>Device Orientation with HTML5</h2>
    6.       You need to be on a mobile device or use a laptop with accelerometer/orientation
    7.       device.
    8.       <p>
    9.       <div id="rawAccel"></div>
    10.       <div id="tiltFB"></div>
    11.       <div id="tiltLR"></div>
    12.       <div id="upDown"></div>
    13.       <imgsrc="http://www.html5rocks.com/en/tutorials/device/orientation/html5_logo.png"id="imgLogo" class="logo">
    14.       <script type="text/javascript">
    15.          if (window.DeviceMotionEvent != undefined) {
    16.          console.log("DeviceMotion is supported");
    17.          window.addEventListener('devicemotion', function(eventData) {
    18.             // Grab the acceleration including gravity from the results
    19.             var acceleration = eventData.accelerationIncludingGravity;
    20.             // Display the raw acceleration data
    21.             var rawAcceleration = "[" + Math.round(acceleration.x) + ", " +Math.round(acceleration.y
    22.             + ", " + Math.round(acceleration.z) + "]";
    23.             // Z is the acceleration in the Z axis, and if the device
    24.             // is facing up or down
    25.             var facingUp = -1;
    26.             if (acceleration.> 0) {
    27.                facingUp = +1;
    28.             }
    29.             // Convert the value from acceleration to degrees
    30.             // acceleration.x|y is the acceleration according to gravity,
    31.             //  we'll assume we're on Earth and divide
    32.             // by 9.81 (earth gravity) to get a percentage value,  
    33.             // and then multiply that by 90 to convert to degrees.
    34.             var tiltLR = Math.round(((acceleration.x) / 9.81) * -90);
    35.             var tiltFB = Math.round(((acceleration.+ 9.81) / 9.81) * 90 *facingUp);
    36.             document.querySelector("#rawAccel").innerHTML =
    37.                                "Raw acceleration" + rawAcceleration;
    38.             document.querySelector("#tiltFB").innerHTML =
    39.                                "Tilt front/back : " + tiltFB;
    40.             document.querySelector("#tiltLR").innerHTML =
    41.                                "Tilt left/right : " + tiltLR;
    42.             document.querySelector("#upDown").innerHTML =
    43.                                "Face Up:Down : " + facingUp;
    44.             updateLogoOrientation(tiltLR, tiltFB);
    45.          }, false);
    46.       } else {
    47.         alert("Not supported on your device or browser. Sorry.");
    48.       }
    49.       function updateLogoOrientation(tiltLR, tiltFB) {
    50.          // USE CSS3 rotations for rotating the HTML5 logo
    51.          //for webkit browser
    52.          document.getElementById("imgLogo").style.webkitTransform =
    53.          "rotate(" + tiltLR + "deg) rotate3d(1,0,0, " + (tiltFB * -1) +"deg)";
    54.          //for HTML5 standard-compliance
    55.          document.getElementById("imgLogo").style.transform =
    56.          "rotate(" + tiltLR + "deg) rotate3d(1,0,0, " + (tiltFB * -1) +"deg)";
    57.       }
    58.    </script>
    59.   </body>
    60. </html>

    Interesting example that uses jQuery mobile

    It shows how the X and Y acceleration values can be used for indicating the sky's direction (vertical), and how the Z acceleration is in fact an indicator for the face up / face down orientation of the device.
    This example has been adapted and put on jsbin.com so that you can tweak it: http://jsbin.com/uyuqek/4/edit
    devicemotion API
    Code from the example:
    1. <html>
    2.    <head>
    3.      <meta http-equiv="content-type" content="text/html; charset=utf-8">
    4.      <meta name="viewport" content="user-scalable=no, width=device-width" />
    5.      <link rel="stylesheet"
    6.            href="http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.css" />
    7.      <script type="text/javascript"
    8.              src = "http://code.jquery.com/jquery-1.6.2.min.js">
    9.      </script>
    10.      <script type="text/javascript"
    11.              src = "http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.js">
    12.      </script>
    13.      <script type="text/javascript">
    14.         $(document).ready(function(){
    15.             window.addEventListener("devicemotion",onDeviceMotion,false);
    16.         });
    17.         function onDeviceMotion(event){
    18.             var ctx = document.getElementById("c").getContext("2d");
    19.             var accel = event.accelerationIncludingGravity;
    20.             $("#sliderX").val(Math.round(accel.x)).slider("refresh");
    21.             $("#sliderY").val(Math.round(accel.y)).slider("refresh");
    22.             $("#sliderZ").val(Math.round(accel.z)).slider("refresh");
    23.             // sky direction
    24.             var angle = Math.atan2(accel.y,accel.x)
    25.             ctx.clearRect(0,0,100,100);
    26.             ctx.beginPath();
    27.             ctx.arc(50,50,5,0,2*Math.PI,false);
    28.             ctx.moveTo(50,50);
    29.             // Draw sky direction
    30.             ctx.lineTo(50-50*Math.cos(angle),50+50*Math.sin(angle));
    31.             ctx.stroke();
    32.         }
    33.      </script>
    34.    </head>
    35.    <body>
    36.       <div data-role="page" id = "intropage">
    37.         <div data-role="header">
    38.            <h1>Accelerometer</h1>
    39.         </div>
    40.         <div data-role="content">
    41.            <label for="sliderX">X Acceleration (Roll)</label>
    42.            <input type="range" name="sliderX" id="sliderX"
    43.                   value="0" min="-10" max="10" data-theme="a" />
    44.            <label for="sliderY">Y Acceleration (Pitch)</label>
    45.            <input type="range" name="sliderY" id="sliderY"
    46.                   value="0" min="-10" max="10" data-theme="b" />
    47.            <label for="sliderZ">Z Acceleration (<strike>Yaw</strike>
    48.               Face up/down)
    49.            </label>
    50.            <input type="range" name="sliderZ" id="sliderZ"
    51.                   value="0" min="-10" max="10" data-theme="c" />
    52.         </div>
    53.         <p style = "text-align:center">SKY direction:
    54.                                       follow this line:</p>
    55.         <div style = "text-align:center;margin-top:10px;">
    56.            <canvas id="c" width="100" height="100"></canvas>
    57.         </div>
    58.      </div>
    59.    </body>
    60. </html>

    MOVE A BALL ON THE SCREEN

    Try this example at JsBin :  if using a mobile device, use this URL instead!
    moving balls
    Code from this example:
    1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    2. <html xmlns="http://www.w3.org/1999/xhtml">
    3.     <head>
    4.        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    5.        <meta name="viewport" content="width=device-width,
    6.                                       target-densityDpi=device-dpi,
    7.                                       initial-scale=1.0,
    8.                                       user-scalable=no,
    9.                                       maximum-scale=1.0">
    10.        <title>iOS 4.2 Device Accellerometer</title>
    11.        <style>
    12.           body {
    13.              font-family:Arial, Helvetica, sans-serif;
    14.              font-size: 14px;
    15.           }
    16.           #board {
    17.              position:absolute;
    18.              left:0px;
    19.              right:0px;
    20.              top:0px;
    21.              bottom:0px;
    22.           }
    23.           #ball {
    24.              position:absolute;
    25.              width: 60px;
    26.              height: 60px;
    27.              border-radius: 30px;
    28.              background-image: -webkit-gradient(radial, 45% 45%, 5, 60% 60%,
    29.                   40, from(red), color-stop(75%, black), to(rgba(255, 255,255, 0)));
    30.              -webkit-box-shadow: 3px 3px 5px #888;
    31.           }
    32.        </style>
    33.        <scriptsrc="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
    34.        </script>
    35.        <script>
    36.           !window.jQuery && document.write('<script src="./js/jquery.min.js"><\/script>')
    37.        </script>
    38.        <script>
    39.           var offset;
    40.           var velocity;
    41.           var board;
    42.           var ball;
    43.           var interval;
    44.           $(document).ready(function() {
    45.               window.addEventListener("devicemotion", onDeviceMotion, false);
    46.               $('#timestamp').html(new Date().toString());
    47.               $('#status').html("Ready!");
    48.               velocity = {};
    49.               velocity.= 0;
    50.               velocity.= 0;
    51.               offset = {};
    52.               board = $('#board');
    53.               ball = $('#ball');
    54.               offset.left = (board.width() - ball.width()) / 2;
    55.               offset.top = (board.height() - ball.height()) / 2;
    56.               $('#ball').offset(offset);
    57.               interval = setInterval(updateBall, 25);
    58.           });
    59.           function onDeviceMotion(event) {
    60.              $('#timestamp').html(new Date().toString());
    61.              $('#status').html("Device Motion Event");
    62.              var eventDetails;
    63.              try {
    64.                  var accel = event.accelerationIncludingGravity;
    65.                  eventDetails = "accelerationIncludingGravity: {" +
    66.                  "<br>     x: " + accel.+
    67.                  "<br>     y: " + accel.+
    68.                  "<br>     z: " + accel.+
    69.                  "<br/>} <br/><br/>" +
    70.                  "interval: " + event.interval;
    71.                  updateVelocity(event);
    72.              } catch (e) {
    73.                  eventDetails = e.toString();
    74.              }
    75.              $('#details').html(eventDetails);
    76.           }
    77.           var decay = .9;
    78.           var bounceDecay = .95;
    79.           var maxVelocity = 100;
    80.           function updateVelocity(event) {
    81.              velocity.+= event.accelerationIncludingGravity.x;
    82.              if (Math.abs(velocity.x) > maxVelocity) {
    83.                  if (velocity.> 0) velocity.= maxVelocity;
    84.                  else velocity.= -maxVelocity;
    85.              }
    86.              velocity.+= event.accelerationIncludingGravity.y;
    87.              if (Math.abs(velocity.y) > maxVelocity) {
    88.                  if (velocity.> 0) velocity.= maxVelocity;
    89.                  else velocity.= -maxVelocity;
    90.              }
    91.           }
    92.           function updateBall() {
    93.              if (offset.left <= -(ball.width() / 2)) {
    94.                 velocity.= Math.abs(velocity.* bounceDecay);
    95.              } else if (offset.left >= (board.width() - (ball.width() / 2))) {
    96.                 velocity.= -Math.abs(velocity.* bounceDecay);
    97.              } else {
    98.                 velocity.= parseInt(velocity.x);
    99.                 velocity.*= decay;
    100.              }
    101.           if (offset.top <= -(ball.height() / 2)) {
    102.              velocity.= -Math.abs(velocity.* bounceDecay);
    103.           } else if (offset.top >= (board.height() - (ball.height() / 2))) {
    104.              velocity.= Math.abs(velocity.* bounceDecay);
    105.           } else {
    106.              velocity.= parseInt(velocity.y);
    107.              velocity.*= decay;
    108.           }
    109.           offset.left += velocity.x;
    110.           offset.top -= velocity.y;
    111.           $('#ball').offset(offset);
    112.       }
    113.     </script>
    114.   </head>
    115.   <body>
    116.      <div id="timestamp"></div>
    117.      <div id="status"></div>
    118.      <div id="details"></div>
    119.      <div id="board">
    120.          <div id="ball"></div>
    121.      </div>spec: <a href="http://dev.w3.org/geo/api/spec-source-orientation.html" target="http://dev.w3.org/geo/api/spec-source-orientation.html">http://dev.w3.org/geo/api/spec-source-orientation.html</a>
    122.   </body>
    123. </html>

EXTERNAL RESOURCES:

No comments:

Post a Comment