Creating Web Apps - The Device Orientation API
Written by Mike James   
Monday, 05 November 2012
Article Index
Creating Web Apps - The Device Orientation API
A Gradient Arrow

 

Usually feature detection is to be preferred to browser detection but in this case there is no obvious way to detect that the directions have been reversed. You could ask the user to tilt the device down to the floor and read the sign of the angle and perform a correction but this isn't a good thing to have to get the user to do.

This isn't a good situation and we can only hope that its fixed soon.

So to correct the beta and gamma angles returned by Chrome we use:

window.addEventListener('deviceorientation',
 function(e) {
   var s=1;
   if(navigator.userAgent.toLowerCase().
                  indexOf("chrome") != -1)s=-1;
   document.getElementById("alpha").
                           innerHTML =e.alpha;
   document.getElementById("gamma").
                        innerHTML = s*e.gamma; 
   document.getElementById("beta").
                        innerHTML = s*e.beta;
   document.getElementById("absolute").
                        innerHTML = e.absolute;
  });

Using this we can now move on and make use of the beta and gamma angles, ignoring any differences between Firefox and Chrome.

A Gradient Arrow

The first interesting thing to try out is to simply draw a line in the direction of the greatest slope - i.e. the direction a marble would roll if it was on placed on the screen of the device.

This is comparatively easy as the two angles should be zero when the device is horizontal - in practice they aren't but its close enough for most applications.

The slope down the y axis is proportional to sin(-beta) and the slope down the x axis is proportional to sin(-gamma). Putting these two together gives us a unit vector pointing down in direction of maximum slope  (sin(-gamma),sin(-beta)).

To draw this vector all we need are some simple canvas instructions but first we need the canvas:

<canvas id="myCanvas"
 width="800" height="800"
 style="border:1px solid #000000;" >
</canvas>

The JavaScript is fairly simple. First a function that draws a line from the center of the canvas to the point x,y:

 

function arrow(x, y,ctx,w,h) {
 ctx.clearRect(0, 0, w, h);
 ctx.beginPath();
 ctx.moveTo(w / 2, h / 2);
 ctx.lineTo(w / 2 + x, h / 2 + y);
 ctx.stroke();
}

We pass in the canvas context and its size width and height. The function clears the canvas, moves to the center and draws a line to x,y. 

Next we need to see a few things up and write the event handler:

var canvas = document.
                    getElementById('myCanvas');
var w=canvas.width;
var h=canvas.height;
var ctx = canvas.getContext('2d');
window.addEventListener('deviceorientation',
    function(e) {
     var s=1;
     if(navigator.userAgent.toLowerCase().
          indexOf("chrome") != -1)s=-1;
     var b=s*e.beta;
     var g= s*e.gamma;
     arrow(w* Math.sin(-g / 180 * Math.PI),
            h * Math.sin(-b / 180 * Math.PI),
            ctx,w,h); },
    false);

Now if you load the page you will see the canvas has a line drawn on it and it "points" down the slope of the device and it gets longer as the slope gets steeper. If it points in some other directions the angles being reported aren't correct.

Roll the Ball

The arrow is a nice demo but can you resist the temptation to roll a ball using the arrow as the direction of acceleration.

This really isn't anything more than the use of the the standard sprite pattern. We start off with a canvas element as before:

<canvas id="myCanvas"
 width="800" height="800"
 style="border:1px solid #000000;" >
</canvas>

And the JavaScript starts off in the same way:

var canvas=document.getElementById('myCanvas');
var w=canvas.width;
var h=canvas.height;
var ctx = canvas.getContext('2d');

Next we need to define a sprite with position, velocity and acceleration. We could also add shape and some general methods to animate it but let's keep things simple and have just a single update method that moves the sprite on one animation step.

var ball={x:w/2,y:h/2,vx:0,vy:0,ax:0,ay:0};
ball.update=function update(ctx){
 ctx.clearRect(0, 0, w, h);
 ctx.beginPath();
 this.x+=this.vx;
 this.y+=this.vy;
 this.vx+=this.ax;
 this.vy+=this.ay; 
 ctx.arc(ball.x,ball.y,40,0,2*Math.PI);
 ctx.stroke();
 if(this.x+40>w)this.vx=-this.vx;
 if(this.x-40<0)this.vx=-this.vx;
 if(this.y+40>h)this.vy=-this.vy;
 if(this.y-40<0)this.vy=-this.vy;

}

The first line sets up the position, velocity and acceleration of the sprite to be zero. The second line defines its update method. This clears the canvas, computes the new position using the velocity and the new velocity using the acceleration. Next a circle is drawn at the new position. Finally the if statements at the end check to see if the sprite has hit the boundary of the canvas. If it has then the velocity in the appropriate direction is reversed to implement a bounce.

Next we need to get the rotation angles in the usual way, but these are going to be used to set the sprite's acceleration.

window.addEventListener('deviceorientation',
 function(e) {
  var s=1;
  if(navigator.userAgent.toLowerCase().
      indexOf("chrome") != -1)s=-1;
  var b=s*e.beta;
  var g= s*e.gamma;
  ball.ax= Math.sin(-g / 180 * Math.PI);
  ball.ay = Math.sin(-b / 180 * Math.PI); },
 false);

Notice that you can't determine how or when the acceleration is updated.

Finally we need to start an animation timer off and call update at a regular time step:

window.setInterval(
 function(){
  ball.update(ctx); },
50);

If you set the time interval too small then the whole process will choke on updates it hasn't the time to carry out. In most cases a 50ms update will produce a smooth animation but if it doesn't try increasing it until things settle down.

Now if you run the program you should see a ball appear in the middle of the canvas and you can make the ball roll in any direction you like by tilting the device. As there is no friction it is possible to get the ball to higher and higher speeds by simply tilting the device following a bounce.

If you would like to allow users to play on desktop browsers or devices without accelerometers all you have to do is hookup the click event handler - something like:

canvas.addEventListener('click',
  function(e){
    ball.ax=(e.clientX-w/2)/(h/2);
    ball.ay=(e.clientY-h/2)/(h/2); },
false);

 

However, it just isn't as much fun.

Conclusion

The device orientation API is just about well enough implemented for you make use of its tilt information to provide user input. However, it is another example of how poorly HTM5 APIs are implemented as soon as you move even a little off the main track. It really is important that browsers not only implement the standards, but manage to do it in a reasonably standard way.

If you would like to take the tilt control idea a little further then why not use Box2D to animate things - see  Getting Started with Box2D in JavaScript.

 

If you would like the code for this article register with I Programmer and visit the CodeBin.

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, Facebook, Google+ or Linkedin,  or sign up for our weekly newsletter.

Getting Started with Box2D in JavaScript

 

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info

 

Banner


JavaScript Jems - Objects Are Anonymous Singletons

JavaScript should not be judged as if it was a poor version of the other popular languages - it isn't a Java or a C++ clone. It does things its own way.  In particular, every object can be regard [ ... ]



JavaScript Canvas - Typed Arrays

Working with lower level data is very much part of graphics. This extract from Ian Elliot's book on JavaScript Graphics looks at how to use typed arrays to access graphic data.


Other Articles

 

 

 



Last Updated ( Monday, 21 January 2013 )