This article provides tips on how to use device orientation information in tandem with CSS 3D transforms.
Using orientation to rotate an element
The easiest way to convert orientation data to a 3D transform is basically to use the alpha, gamma, and beta values as rotateZ
, rotateX
and rotateY
values. There are however two corrections that should be applied to those values:
- The initial alpha value is 180 (device flat on the back, top of the screen pointing 12:00), so the
rotateZ
value should be alpha - 180 - The Y axis of the screen coordinate system is inverted, such that translateY(100px) moves an element 100px down, so the
rotateY
value should be -gamma
Finally, the order of the three different rotations is very important to accurately convert an orientation to a 3D rotation: rotateZ
, then rotateX
and then rotateY
. Here's a simple code snippet to sum it up:
var elem = document.getElementById("view3d"); window.addEventListener("deviceorientation", function(e) { // remember to use vendor-prefixed transform property elem.style.transform = "rotateZ(" + ( e.alpha - 180 ) + "deg) " + "rotateX(" + e.beta + "deg) " + "rotateY(" + ( -e.gamma ) + "deg)"; });
Orientation compensation
Compensating the orientation of the device can be useful to create parallax effects or augmented reality. This is achieved by inverting the previous order of rotations and negating the alpha value:
var elem = document.getElementById("view3d"); window.addEventListener("deviceorientation", function(e) { // again, use vendor-prefixed transform property elem.style.transform = "rotateY(" + ( -e.gamma ) + "deg)" + "rotateX(" + e.beta + "deg) " + "rotateZ(" + - ( e.alpha - 180 ) + "deg) "; });
You can test this code in an "orientation-aware" mobile browser such as Firefox Mobile with this demo of orientation compensation.
rotate3d to orientation
Should you ever need to convert a rotate3d axis-angle to orientation Euler angles, you can use the following algorithm:
// convert a rotate3d axis-angle to deviceorientation angles function orient( aa ) { var x = aa.x, y = aa.y, z = aa.z, a = aa.a, c = Math.cos( aa.a ), s = Math.sin( aa.a ), t = 1 - c, // axis-angle to rotation matrix rm00 = c + x*x * t, rm10 = z*s + y*x * t, rm20 = -y*s + z*x * t, rm01 = -z*s + x*y * t, rm11 = c + y*y * t, rm21 = x*s + z*y * t, rm02 = y*s + x*z * t, rm12 = -x*s + y*z * t, rm22 = c + z*z * t, TO_DEG = 180 / Math.PI, ea = [], n = Math.sqrt( rm22 * rm22 + rm20 * rm20 ); // rotation matrix to Euler angles ea[1] = Math.atan2( -rm21, n ); if ( n > 0.001 ) { ea[0] = Math.atan2( rm01, rm11 ); ea[2] = Math.atan2( rm20, rm22 ); } else { ea[0] = 0; ea[2] = ( rm21 > 0 ? 1 : -1 ) * Math.atan2( -rm10, rm00 ); } return { alpha: -ea[0] * TO_DEG - 180, beta: -ea[1] * TO_DEG, gamma: ea[2] * TO_DEG }; }