Some Guy Named Dylan    




Home

Lab

Links

Contact
 







Fun With Transform Origins and Matrices



Forcing IEto support a transform origin, transform origin IE. Plus a transform animation  class for YUI 2.7.

Contents...



Implementing Transform Origin In IE+ Matrix Math

Examples + Source Code

Code Sample

YUI Matrix Animation Class

Limitations with IE

Other Findings

References, Inspiration


Implementing Transform Origin In IE


The key to this is IE solution is that as the matrix filter is applied to the element, its offsetWidth and offsetHeight  are the dimensions of the transformed element's bounding box if and only if  the sizing method parameter is set to 'auto expand'. 

.matrix-transform{
 filter: progid:DXImageTransform.Microsoft.Matrix(M11='0.7071',M12='-0.2479',M21='0.7071',M22='1.1663', sizingMethod='auto expand');
 
} 

If'auto expand' is not used, then you will have to write code that finds the dimensions of the  bounding box of the transformed element and manually set the width and height yourself. If you use  'auto expand' then IE will resize the element for you free of charge.  

Another issue is that in IE the element needs to be positioned absolutely so the element will be removed  from the document flow. As a workaround I wrap the element in a relatively positioned element sized  to the same dimensions.  

So with this in mind to support a transform origin in internet explorer the following steps must be taken:  Note: for this example we'll be trying to achieve the following styling on an element:  
 div.example {
  transform: rotate(45deg) skewX(33deg);
  transform-origin(150% 33%);
  width:100px;
  height: 100px;
 }

The element's bounding box will be a transparent blue and has the dimensions of width:97px, height:187px

The transformed element will have a containing bounding box's left and top properties set in order to  illustrate each step.

A rotation of 45 degrees, and skewing about the x axis 33 degrees will result in the following matrix  (I've truncated to 4 decimal places):  
0.7071 -0.2479
0.7071 1.1663

Let's proceed with the implementation steps.



Always have the original dimensions of the element handy.
  width:100px;
  height: 100px;
 








Take the original center of the element and translate it by the negated transform origin.
 center = (50, 50)
 origin = (100 * 150%, 100 * 33%) = (150, 33)
 translated center = (50 - 150, 50 - 33) = (-100, 17)
 








Apply the matrix transform to the result.
 
 [[0.7071, -0.2479],
  [0.7071, 1.1663]]
  * [-100, 17]
  = [0.7071 * -100 + -0.2479 * 17, 0.7071 * -100 + 1.1663 * 17]
  = [-74.9242, -50.8834]








Translate the result by the transform origin.
 [-74.9242 + 150, -50.8834 + 33]
 = [75.0758, -17.8834]
 








Subtract from the x value of the result half the width of the bounding box,  and from the y value of the result half the height of the bounding box.
 [75.0758 - 97/2, -17.8834 - 187/2] ~= [26.5, -112]
 








This is where the top left corner of the bounding box should be

Actual Code:
 #true-transform {
 -moz-transform: rotate(45deg) skewX(33deg);
 -moz-transform-origin: 150% 33%;
 -webkit-transform: rotate(45deg) skewX(33deg);
 -webkit-transform-origin: 150% 33%;
 





Mathematically, this is nothing new, it's really just the algorithm for  transforming a point or set of points (think corners of a polygon) about an origin which is:


(一)Translate by the negated value of the origin.

(二)Apply the transform to the points, i.e the matrix.

(三)Translate by the origin.
 



The tough part is the origin dimensions, I don't have a viewport change listener or anything  going on, so if the screen is resized in anyway then the effects won't be consistent in ie since  the code has sized the element but all in all I think it's working pretty well.  

Examples


Here are some rudimentary examples:


Origin Manipulation through Drag 'n Drop  

Tabview POC using matrix animation  

Download the Code  
Just Updated 1/07/2011. Could not transform properly in IE because my code was shortsighted  and couldn't parse floating point numbers. Never fear, this POC code works now :)

Just updated 03/10/2010. Could not rotate by increments of 90 degrees due to  css style object's choking on really small numbers in exponential format, i.e  6.2345197345e-17.
 Thanks to Denny Wong for finding the issue.  




Coding


To apply transform styles element by element I created a utility object named ElementTransformer  that adds in some nice fluentness for readability. For convenience I found typing new ElementTransformer  to be a pain so using a function caled elTransform makes things a bit more wrist friendly.

This will enable you to write code like:
 elTransform('id-of-element').
 origin('left top').
 matrix({rotate:60/*degrees*/});

All methods that require an angle are normalized to degrees. You're welcome to modify the code as you see fit, personally I find working with bulky floating point numbers due to radians a pain in the wrist.

Another thing is all code (minus the MatrixAnim class) is library agnostic. You're free to drop it in and muddle with it as you see fit. Using a solid library to handle some of the plumbing will probably stablize the code even more.

Animation Class


I also wrote an animation class utilizing YUI 2.7.0.

The code is along the same lines as the coding conventions used there like:
  var anim = new MatrixAnim('some-element', {
                rotate: {
                    by: 360
                },
                scale :{by:0.25},
                skewY : {from:10, to:45}
            }, 3, YAHOO.util.Easing.bounceOut);
 
 //kick off the animation
 anim.animate ();


Limitations with IE



The element must be rendered in order for the transform origin to work properly.  Note: It does not need to be visible though.  

Only works with block level elements like div, p, table, h1-6.  

Trying to transform a table row or table cell element or a span nested within an anchor tag will result in failure.
Why?

Because IE requires some work arounds. My solution entails wrapping the transformed element  with a relatively positioned div sized to the same dimensions. This wrapper element  allows: 


Layout preservation.  

transform origin support because the absolutely positioned element will now be positioned  relative to its parent.  



In ie 8, absolutely positioned elements inside of the transformed element are not transformed.  They are in version ie 6 and 7 though.  



Other Findings


I found an issue with Mozilla's documentation on the matrix method in css.  

From the documentation at https://developer.mozilla.org/en/CSS/-moz-transform#matrix  on 7/22/2009  
-moz-transform: matrix(a, b, c, d, tx, ty)

/* Where a, b, c, d build the transformation matrix 
a b
c d

and tx, ty are the translate values.*/  

The problem is evident in that the resultant matrix of a 45 degree rotation rounded to 4 decimal places is:  
0.7071 -0.7071
0.7071 0.7071

If we had an element with the dimensions of 100x100 pixels it would have the following coordinates:  

[[0,0], [100,0], [100,100], [0,100]]  

Applying a 45 degree matrix to each point (transform origin at 0,0) yields:  

[[0,0], [70.71, 70.71], [0, 141.2], [-70.71, 70.71]]  

Applying this matrix in firefox 3.5 or webkit browsers it will rotate the element counter clockwise.  


-moz-transform: rotate(45deg); 
-webkit-transform: rotate(45deg);


I am an example
 45 degree rotation  




-(moz|webkit)-transform: matrix(0.7071, -0.7071, 0.7071, 0.7071, 0, 0); 


I am an example
 With an equivalent 45 degree matrix,  But I'm going the wrong way.  



This is an easy fix

Given a matrix  
a b
c d

You merely apply it by columns, not rows and this will give the correct result.  

-(moz|webkit)-transform: matrix (a, c, b, d, dx, dy) 


-moz-transform: rotate(45deg); 
-webkit-transform: rotate(45deg);


I am an example
 Been rotated 45 degrees  




-(moz|webkit)-transform: matrix(0.7071, 0.7071, -0.7071, 0.7071, 0, 0);


Now I'm rotated the right way.  




Some other nuances between firefox and safari. If you apply the transform to an element and query the element's position,  for instance this element above Firefox will consistenly return 101 x 101.  

Webkit's results are very inconsistent, I wasn't able to track down a pattern or any rhyme or reason to it. In my origin  manipulation example I had to get the region of the animated element  outside of my drag drop end handler. I found if I got the region of the element in the handler while the  element was being animated the element would shoot all over the place.  

Last Modified 9/18/2009 4:33 PM PST



References, Inspiration


 I was really inspired to do this after seeing these uses of IE's CSS filter  property to support matrix transformations.


Bringing CSS Transforms to IE

The coverflow effect achieved here is really impressive.  


http://blogs.microsoft.co.il/blogs/nir_tayeb/archive/2008/10/29/fx-matrix.aspx

Great animation, I ended up writing a matrix animation class for YUI 2.7.  


Birdmanizer

Totally cool, no IE support though, but there is work being done for a jQuery patch: http://www.zachstronaut.com/posts/2009/02/22/jquery-patch-css-transform.html
 

MSDN Matrix Filter Documentation  

A Great JavaScript Docmentation Resource
 






   





© Dylan Oudyk 2007-2012