Get Value of CSS Rotation through JavaScript

Avatar of Chris Coyier
Chris Coyier on (Updated on )

A comment by aditya:

Is there a way to get the angle [through JavaScript] by which the element is rotated?

Seems like a reasonable request. So we have some HTML:

<div id="i-am-rotated">text</div>

And it’s rotated through CSS:

#i-am-rotated {
  -webkit-transform: rotate(30deg); 
  -moz-transform:    rotate(30deg); 
  -ms-transform:     rotate(30deg); 
  -o-transform:      rotate(30deg);  
}

Our goal is to get the number “30” from that element via JavaScript. The modern way to access styling information from an element is getComputedStyle() (Supported in all modern browsers and IE 9+, older IE supported currentStyle()). Let’s try and get it with getComputedStyle():

var el = document.getElementById("i-am-rotated");

var st = window.getComputedStyle(el, null);

var tr = st.getPropertyValue("-webkit-transform") ||
         st.getPropertyValue("-moz-transform") ||
         st.getPropertyValue("-ms-transform") ||
         st.getPropertyValue("-o-transform") ||
         st.getPropertyValue("transform") ||
         "Either no transform set, or browser doesn't do getComputedStyle";

You might think the value returned would be “rotate(30deg)” and we could run parseInt() on it and get “30”. But unfortunately that doesn’t work. The actual value we get back is this:

console.log(tr);
// matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)

The browser turns the CSS rotation transform into a matrix transform. I imagine it does this to simplify what could be multiple transforms on the single element into one value. So what are we to do?

Nicolas Gallager researched the matrix transformation for rotate transforms. Which is essentially this:

rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0);

We really just need one of these to make a quick equation. We need to get the arcsin (inverse of sin, sin-1) of the values, making sure to get it in radians.

First we get our hands on the separate individual matrix values:

// UPDATE: below was causing errors sometimes...
// var values = tr.split('(')[1].split(')')[0].split(',');
// Replace with... (thanks Thierry)
var values = tr.split('(')[1],
    values = values.split(')')[0],
    values = values.split(',');

var a = values[0]; // 0.866025
var b = values[1]; // 0.5
var c = values[2]; // -0.5
var d = values[3]; // 0.866025

Then we know sin(X) == 0.5 so asin(0.5) == radians and degrees == radians * 180/π.

So:

var angle = Math.round(Math.asin(b) * (180/Math.PI));
console.log(angle);
// 30

Yay!

Nicolas took it a bit further by accounting for scale as well. With the full code below, the rotation value can be extracted with any number of other transforms applied.

#complex-transform {
  -webkit-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px); 
  -moz-transform:    rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px); 
  -ms-transform:     rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px); 
  -o-transform:      rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);  
}
var el = document.getElementById("complex-transform");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
         st.getPropertyValue("-moz-transform") ||
         st.getPropertyValue("-ms-transform") ||
         st.getPropertyValue("-o-transform") ||
         st.getPropertyValue("transform") ||
         "fail...";

// With rotate(30deg)...
// matrix(0.866025, 0.5, -0.5, 0.866025, 0px, 0px)
console.log('Matrix: ' + tr);

// rotation matrix - http://en.wikipedia.org/wiki/Rotation_matrix

var values = tr.split('(')[1];
    values = values.split(')')[0];
    values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];

var scale = Math.sqrt(a*a + b*b);

// arc sin, convert from radians to degrees, round
// DO NOT USE: see update below
var sin = b/scale;
var angle = Math.round(Math.asin(sin) * (180/Math.PI));

// works!
console.log('Rotate: ' + angle + 'deg');

UPDATE: Turns out this line is way more reliable for calculating angle:

var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));

Thanks Nicolas, Divya, and Oli for behind-the-scenes help.