Correcting Transform Origin and Translate in IE

Pages 5

Clone this wiki locally

The matrix filter in IE has some severe limitations, not the least of which is that matrices are not something most people are used to calculating. While calculating a matrix can be easily done in JavaScript using the Sylvester library, IE also lacks support for transform-origin and translate(). This means it’s not an easy task to make a transformation look the same in IE as it does in Mozilla or WebKit.

The jQuery Transform plug-in handles these calculations automatically. There are a few articles that got me going on this:

Correcting Transform Origin

IE kind of locks the transform origin at 0, 0. However, IE also glues the top-most and left-most pixels of the transformed object to the top and left of the original object. This means that the top-left corner of the transformation is rarely where you’d expect it to be; you can’t simply set the transform-origin to 0, 0 in the other browsers. Correcting for this is not an easy task but the core of the solution relies on relative positioning; calculating the top and left takes some math.

transform-origin: left top; in other browsers

default in IE

This is what a top-left origin looks like, but this is not what it looks like in IE by default.

Making IE play nice

Remember the height and width! (IE forgets)

Calculate a matrix (we’ll use a matrix for rotate(45)) and apply it to the element

Ensure the element is positioned

Calculate the offset between a 0, 0 origin and our desired origin (50%, 50% is the default in non-IE browsers)

Calculate the new top and left of the transformed element

Set top to offset.y + ty + newTop

Set left to offset.x + tx + newLeft

Remember the height and width

After a matrix is applied to an element IE reports the height and width of the new element accurately which is actually wrong. In all other browsers, a 100px by 100px <div> continues to think it is that size no matter how it is transformed. IE on the other hand returns the resulting dimensions. So if a 100px by 100px <div> is rotated 45deg, IE correctly reports that the <div> is actually 141px by 141px. But this is a huge hassle if we’re trying to run the original <div>’s dimensions through a matrix to figure out how to re-position it.

There are two potential solutions to the issue:

Remember $('.example').height(), $('.example').width(), $('.example').outerHeight() and $('.example').outerWidth() in variables. (This is how I fixed this initially.)

Remove the matrix temporarily, read the required dimensions, and then re-apply the matrix. (This is how I’ll fix this the next time I work on the plug-in.)

Calculate a matrix

For this example, we’re going to rotate a <div> 45 degrees. For fun, we’ll start working on a crazy-simple and not very useful plug-in for doing our rotation. Below you’ll see that our example plug-in only supports rotate and only works in IE.

Here you can see the strange idea IE has of the default origin. This looks particularly funny if you animate it. The top-most and left-most edges of the pink box always stay glued the the top and left of the gray box.

Calculate the origin offset

We don’t want to be stuck with IE’s sort-of-top-left origin, we want to use the 50%, 50% origin every other browser uses. We need to use Sylvester to create a matrix that we can use to transform some coordinates. Below you’ll see a function that uses the matrix we calculated to approximate a transform-origin(50%, 50%) in IE. Again, we’re hard-coding as much as possible to make this code easier to understand.

Here you can see that we’ve correctly centered the origin however, IE has pushed it way to the right because it uses the wacky boundary origin that we saw above. We need to pull the element back to the left.

Calculate the new top and left of the transformed element

Now that we’ve repositioned the element, it’s still in the wrong place. As we noted above, IE will fix the top-most and left-most edges of the element to the original top and left coordinates. To undo this we need to calculate the new positions of all of the corners and determine the new position of the top-most and left-most edges. We do this by plugging all of the coordinates into our matrix and transforming them.

NOTE: We’re recalculating the height and width and using wrap() and unwrap() simply because we’re being lazy and not remembering stuff we’ve calculated previously. So, don’t get too distracted by that; in real code you’d save everything in variables and pass it around instead of constantly asking the DOM for it.