boogdesign posts

Longer posts on standards based web design, portable web development and Linux, intermingled with some stuff on my other nerd interests.

Rob Crowther, London, UK based Blogger, Web Developer, Web Designer and System Administrator - read my Curriculum Vitae

Buy my book!

Book CoverHello! HTML5 and CSS3 available now

Buy my other book!

Book CoverEarly access to HTML5 in Action available now
« Adventures in Web 3.0: Part 3 - More CSS 3 :: Rounded Corners for Image Elements in Firefox with SVG »

Arbitrary Element Rotation in IE - the Matrix Filter

04/09/09

12:38:03 am Permalink Arbitrary Element Rotation in IE - the Matrix Filter

Categories: Front End Web Development, Standards, HTML and CSS

There was a post on WebDesign-L today asking about angled text, which reminded me of the excellent Text Rotation with CSS a few months back on snook.ca. Jonathan focussed on the IE only BasicImage filter to provide an alternative to the (also proprietary but future standard) -webkit-transform and -moz-transform properties to rotate text. That's fine as far as it goes, but BasicImage can only rotate in increments of 90. Jonathan mentions the alternative, the Matrix filter, but says "the coordinates still don't make any sense to me."

Now, WebKit and Gecko also support matrix transformations, so I'm thinking if I can only work out what the co-ordinates mean we might have the basis of a more general purpose solution. So I looked at the Wikipedia examples of linear transformation matrices and then the Mathamazement introduction linked to from the MDC page. If you can hear a loud whooshing sound right now, that's the echo of a load of math stuff flying completely over my head...

However, all was not lost - if we go back to the MSDN Matrix transform article you'll see there's some example code. With something to hack around I thought I had a much better chance of figuring out how things worked. I started off with the fourth sample, it uses Javascript to animate the Matrix transform, rotating and expanding a div element.

I modified the code a bit, but I'll take you through the key points before I show you what I finished up with. First of all, the Matrix transform needs to be defined on the element in question before you can script it:

<DIV ID="oDiv" STYLE="position:absolute;
filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand')"
		onfilterchange="fnSpin(this)" >

The animation is powered by fnSpin which calls two subsidiary functions, fnResize to resize the image and fnSetRotation to rotate it. Here's the original code for the rotation:

//oObj input requires that a matrix filter be applied. 
//deg input defines the requested angle of rotation.
var deg2radians = Math.PI * 2 / 360;
function fnSetRotation(oObj, deg)
{    rad = deg * deg2radians ;
    costheta = Math.cos(rad);
    sintheta = Math.sin(rad);

    oObj.filters.item(0).M11 = costheta;
    oObj.filters.item(0).M12 = -sintheta;
    oObj.filters.item(0).M21 = sintheta;
    oObj.filters.item(0).M22 = costheta;

}

So here's it all nicely laid out with all the hard math stuff already done for us. The basic math stuff is it's converting the degrees into radians, then using sine and cosine functions to calculate the four matrix values. So now I'm thinking I can reverse engineer this function to work out what the matrix values will be for any angle. I started off with modifying the fnSetRotation so that the matrix values were visible on the page - adding four text inputs and these lines:

    document.getElementById("fM11").value = oObj.filters.item(0).M11;
    document.getElementById("fM12").value = oObj.filters.item(0).M12;
    document.getElementById("fM21").value = oObj.filters.item(0).M21;
    document.getElementById("fM22").value = oObj.filters.item(0).M22;

Then I added a button which rotates the element to 45 when clicked (warning, IE only):

Rotate element by 45 degrees in IE

The next step was to make the page work cross browser, accept a value for the input number of degrees and, for bonus credits, generate the relevant CSS for copy and pasting. First of all I cleaned up the output a bit with toFixed method as, when the values got close to zero, they end up in exponent format rather than just rounding to 0. This gave me numbers I could plug straight into a CSS template, for example here's the declaration to rotate an element by 45:

filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);

So now I thought it would be a fairly straightforward task of plugging these values into the Mozilla format:

-moz-transform:  matrix(a, b, c, d, tx, ty)

The tx and ty are for translation, so I just set them to zero, then plugged M11, M12, M21 and M22 into a, b, c and d respectively. Unfortunately, it didn't work! The element was reflected in respect to IE in Firefox for certain rotations.

Someone who actually understands what's going on with the matrices could probably explain what's happening (probably I have the co-ordinate system the wrong way up?) but I fell back on trial and error and eventually decided that swapping b and c worked. See my final matrix calculator here - input an angle in degrees, click the button and see the CSS code for achieving it in IE, Gecko and WebKit.

Some things I noticed but didn't have time to investigate fully:

  • IE seems to rotate around the center, Firefox and Chrome seem to rotate from top left. There's a -moz-transform-origin and equivalent WebKit property to control things in those browsers, not sure what equivalents IE offers.
  • Other CSS declarations are applied after the transform in IE, while they seem to be applied before the transform in Firefox and Chrome (ie. if you add 200px padding to the top of an element, Firefox will rotate around a point 200px above the 'start' of the element, IE will apply 200px padding after the transform).
  • Using the rotate syntax in Gecko and WebKit is a lot simpler, but if you want to rotate stuff in IE too using a similar format in all three might be more simple in the long run? Especially if you want to script it.

Tweet this!
PermalinkPermalink

Comments:

Comment from: Shea Frederick [Visitor] · http://www.vinylfox.com
04/09/09 @ 13:09
Good job figuring this out. Im with you on the math - totally over my head.
Comment from: Mathieu 'p01' Henri [Visitor] · http://www.p01.org
04/09/09 @ 15:20
o_O wasn't this all documented and demonstrated 9 years ago in the MSDN page you linked to ?

Regarding the origin of the transformation, there is the Dx and Dy attributes to translate the element.
Comment from: robertc [Member] · http://www.boogdesign.com/
04/09/09 @ 15:52
Hi Mathieu

The MSDN page demonstrates how to set the different matrix values rather than what specific matrix values you need to put in to (say) rotate by 30. My aim was to provide a tool which lets you work out what the matrix values are for a particular rotation. You're correct that you could have worked it out for yourself by just doing the maths.

By the origin of the transformation I meant being able to rotate the element around its bottom left corner rather than around its centre point. I guess being able to move it and rotate it is equivalent if you calculate Dx and Dy in terms of the size of the element to be rotated?

Rob
Comment from: Andrea Giammarchi [Visitor] · http://webreflection.blogspot.com/
05/09/09 @ 20:47
Maybe you guys would like to use a cross browser function which aim is to rotate without useless html progid styles?

http://www.devpro.it/code/199.html

Regards
Comment from: Jimmy Taylor [Visitor] · http://www.recoverybull.com
18/09/09 @ 13:12
It is nice to view all the images and the mathematical algorithm it is simply so difficult by the user as static over to my head....
Thanks
Jimmy Taylor
Comment from: Thomas [Visitor]
14/11/09 @ 16:47
Thanks a lot for the calculator!
One detail: It would be nice if this thing was wrapped into a form and submittable by hitting enter.
Comment from: robertc [Member] · http://www.boogdesign.com/
14/11/09 @ 19:14
Thanks Thomas. The calculator page is definitely a bit raw, I'll try and set aside some time for tidying it up a bit :)

Rob
Comment from: robertc [Member] · http://www.boogdesign.com/
29/12/09 @ 12:06
I've updated the calculator page: it now responds to hitting return within the input box as the function is called on the form submit event instead of the onclick of the button and I've added in support for Opera (10.50 onwards).

Rob
Comment from: Daniel [Visitor] · http://danielhellier.com
31/12/09 @ 04:19
I have this setup on a hover of a link, but it seems to make the image cut off to the left when it rotates. It's driving me nuts. :(
Comment from: robertc [Member] · http://www.boogdesign.com/
22/01/10 @ 00:54
I've made some improvements to the calculator page. The two practical differences are I've added the IE8 style -ms-filter syntax and I've added SizingMethod of 'auto expand' to the output - this means the element will resize to accommodate the rotated version. Daniel, I think this is what was causing your cut off issue - it was set in the CSS of the calculator page but not included in the output of the tool.

Rob