Grow your CSS skills. Land your dream job.

Get Value of CSS Rotation through JavaScript

Published by Chris Coyier

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 values = tr.split('(')[1];
whatever = tr.split(')')[0];
whatever = tr.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));

View Demo

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

Comments

  1. So good!

    scale() and rotation() ok! but…

    What about skew() and translate()?

  2. I’ll probably never use this, but it was interesting to see that the computer uses a matrix to calculate the rotation, and not radians or degrees or something.

  3. Adam
    Permalink to comment#

    I think that there is something missing from the calculations.

    I modified the fiddle to put a 130 degree rotation on (an then moved it some more so you could see it) and the script reported a 50 degree rotation:

    http://jsfiddle.net/459jS/98/

    • Great catch!

      Looks like factoring in the new math that Nicolas linked to above, we can change the arc sign stuff to this:

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

      and it’s returning good results for all angles I’ve tried, including the problem Agos presented above.

  4. Hey, anyone checking this out might be interested in Easy Transform. It’s nowhere near as powerful or useful as it could be. Fork it and add to it if you can, or just general feedback and usage would be great:

    Easy Transform @ Github

  5. Permalink to comment#

    Ahh, I remember doing similar things in Java3D – Converting the matrix back into a rotation angle.

    Is it the same in Firefox and Opera?

  6. Jay
    Permalink to comment#

    awesome

  7. Permalink to comment#

    Math is so much fun :)
    Great post, Chris, and also great analysis, Nicolas.

  8. Permalink to comment#

    Wow, that seriously needs to be a jQuery method.

  9. Mike
    Permalink to comment#

    I don’t know how long I have been waiting for this, but it has been a while.

    I may be some time.

    Thank you

  10. Great article! Thanks for the advice, greatly appreciated!

  11. pilus
    Permalink to comment#

    hai, this might be just me, but why can’t we just use:

    ele.style['-webkit-transform']

    or something similar, in my chrome console, it returns:

    rotate(90deg)

    amy I missing something here ?

  12. I was having issues with this code when I needed to get a rotation value above 180 degrees.

    Turns out the atan2 function only returns a value from -pi to pi, so you only get degree values from -180 to 180. For example, when I tried to get a rotation of 270deg the code was returning -90.

    The only change is to add a check to see if the result in radians is less than zero, and if so, add (2 * pi) to the result

    var radians = Math.atan2(b, a);
    if ( radians < 0 ) {
      radians += (2 * Math.PI);
    }
    var angle = Math.round( radians * (180/Math.PI));
    

    See my pen here for a comparison: http://codepen.io/jjeaton/pen/bzolH

  13. I think the examples are slightly wrong (not for the main article content but for the scale stuff) plus there is a bit of a random code going on!

    // 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(',');
    
    // THIS BIT CAN GO – UNLESS I'M MISSING SOMETHING
    // var values = tr.split('(')[1];
    // whatever = tr.split(')')[0];
    // whatever = tr.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, as far as I can tell (its really rarely / mathematically documented) matrices work like the following:

    matrix(scaleX, skewX, skewY, scaleY, translateX, translateY);
    

    So this next bit gets really confusing (the last example):

    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];
    
    // I'm sure this isn't right
    var scale = Math.sqrt(a*a + b*b);
    

    I would write it as:

    var scaleX = values[0];
    var skewX = values[1];
    var skewY = values[2];
    var scaleY = values[3];
    var translateX = values[4];
    var translateY = values[5];
    

    For a start, it stops us just mapping the values[0], values[1], values[2] etc to the equally unhelpful a, b, c but importantly shows us that the var scale = Math.sqrt(a*a + b*b); line can simply be removed, as we already have scale, in each dimension. Also, if we use that example then the scale = sqrt((0.866025 * 0.866025) + (0.5 * 0.5)) =
    0.9999996503

    But in fact, we know the matrix itself says the scale is 0.8660254037844387 on the x, and is 0.8660254037844387 on the y, so I’m guessing that number should read 0.8660254037844387. (That equation used finds the hypotenuse of a triangle using incorrect points unless I’m being stupid!)

    Just starting to learn about these so I hope I’m correct, very embarrassed if not. Feel free to delete this comment if you incorporate into the article : )

  14. cristian levrero
    Permalink to comment#

    Gracias!! me salvaste de horas de romperme la cabeza!!
    Thank you! saved me hours of head-breaking!

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".