为之漫笔

为之漫笔正在翻译

The CSS3 matrix() Transform for the Mathematically Challenged

原文链接: www.useragentman.com

Notes:

  • This article was not rendering correctly in Firefox 4.x due to a change in how that browser handles MathML. This issue has now been fixed as of May 1, 2011.
  • If you are not familar with 2D-Transforms, take a look at my other article Cross Browser CSS Transforms – even in IE

Screenshot of the Matrix Construction Set

The Matrix Construction Set allows you to make a pixel perfect transformation by just dragging and dropping objects around.

The CSS3 transform property can do some really cool things - with it, web designers can rotate, scale, skew and flip objects quite easily. However, in order for deisgners to have fine-grained, pixel level control over their transforms, it would be really helpful to understand how the matrix() function works. With the matrix() function, designers can position and shape their transformations exactly where they want to.

The problem is, not many people actually understand what the numbers in the matrix filter actually mean. To solve this issue, I have created a tool, which I call the CSS3 Matrix Construction Set that can take any block-element, positioned anywhere on the page, transform it anywhere else on the page using the matrix() filter, and reveal the correct CSS code for this operation. Look at the tool now and if that is all you are interested in, you can stop there. However, if you what to know what the numbers in the matrix() function actually mean, and why you want such knowledge in the first place, read on. I'll try to explain this as simply as possible (and please feel free to use the feedback form if you have any opinions on how to make this article better).

Take a Look at the CSS3 Matrix Construction Set

Note: This tool works in all modern versions of Firefox, Sarari and Chrome. Although it is not usable in Opera (due to its lack of support for HTML5 drag and drop) the CSS code it produces in other browsers is usable in that browser. The tool also does not work in IE since neither that browser, nor my CSS3 polyfill library, cssSandpaper, support the transform-origin property. I hope to fix cssSandpaper to implement this in a future release, but in the meantime, please read the note at the end of this article for a workaround for this issue.

Matrices: Why Should I Care?

Matrices are to transforms like RGB hex codes are to colors: they are representations that are easy for computers to understand, but not human beings. Sure, web designers can use the rotate(), skew(), scale() and translate() functions to fulfill their transformation needs ... why should we bother with matrix() at all? There are a few cases when you may want to:

  • Brevity:

    Using matrices, it is possible to represent a complex string of 2D transforms like this:

    #object {
        transform-origin: 0 0;
        transform: rotate(15deg) translateX(230px)  scale(1.5, 2.6) skew(220deg, -150deg) translateX(230px)
    }
    
    

    using one matrix() rule like this:

    #object {
        transform-origin: 0 0;
        transform: matrix(1.06, 1.84, 0.54, 2.8, 466px, 482px)
    }
    
    

    (Note: I have ignored the vendor-specific variants (e.g. moz-transform, etc.) for the sake of brevity).

  • Pixel-Perfection:

    If you know exactly how you want your transformation to look, it'll take a bit of fiddling around to get it to look how you want using the other transform functions, and it may not be pixel perfect if you lack patience (like I do). However, using a tool like the Matrix Construction Set, you can place the transform exactly where you want (This is analogous to using a color wheel to choose RGB colors for you, instead of using keywords like red, green or peachpuff).

  • JavaScript:

    Revealing an object's transform information using JavaScript's getComputedStyle() function will yield a matrix() function in all current web browsers that support CSS3 transforms, even if it was rendered using other transform functions like rotate(). For example, the object below has been rotated 45° using transform: rotate(45deg):

    But if you get the object's computed style, that will reveal that internally it stores a matrix() function.

Ok, What Does The Markup Look Like?

The matrix() function takes 6 parameters in order for it to work:

#transformedObject {
    -moz-transform:    matrix(1.4488, -0.3882, 0.3882, 1.4489, 400px, -100px);
    -webkit-transform: matrix(1.4488, -0.3882, 0.3882, 1.4489, 400, -100);
    -o-transform:      matrix(1.4488, -0.3882, 0.3882, 1.4489, 400, -100);
    transform:         matrix(1.4488, -0.3882, 0.3882, 1.4489, 400, -100);
}

Note the difference with the Firefox implementation of matrix() — the last two elements need the px units after it. For now, think of it is a difference in notation, but we'll explain why this is later.

But What Do The Numbers Mean?

In order to explain what they mean, I will have to define a few math-concepts here. Don't panic if you are mathematically challenged. These concepts are not that hard to understand, but there is a bit of explanation needed. Why should you torture yourself if you hate math?

  • If you are a designer:

    Think of the stuff below as informational. If you use the Matrix Construction Set you won't need to calculate anything by hand (but wouldn't you want to have an idea what the numbers mean anyway?)

  • If you are a JavaScript developer:

    This information will be invaluable when optimizing scripts that rely on transformation effects. Just try to understand the basic concepts here, and if you still need help, use the Sylvester JavaScript library to do the heavy lifting for you.

  • If you want to be an über-geek:

    All those matrix jokes you hear at parties will suddenly start to make sense!

    In fact, draw all your rotational matrices sideways. Your professors will love it! And then they'll go home and shrink.

    Finally you'll be able to understand why all the geeks at work find this XKCD comic so funny.

Terminology

Matrix

The easiest way to think of a matrix is as a group of numbers written in a rectangle or square. For our purposes, we will be dealing with 3x3 matrices, such as this one below:

That's nine numbers! So how come the CSS3 matrix() function only has six?

#transformedObject {
     transform:  matrix(1, 2, 3, 4, 5, 6);
}

For CSS3 2D transforms, we only deal with 3x3 matrices that have the two bottom-left numbers that are equal to zero, and the bottom-right value equal to 1. As a result, these two notations are equal:

Dot Product

Next we are going to explain what a dot product is. At first, this may not seem like this is related to matrices at all, but I promise it does ... just read on and you'll understand why in no time. Trust me. :-)

Let's say you have two (x, y) points, ( 1 2 ) and ( 4 5 ) . The dot product of these two points (written ( 1 2 ) · ( 4 5 ) ) is what you get when you multiply the two x-coordinates, multiply the two y-coordinates, and then add them together:

This doesn't only work for 2-dimensional coordinates, but also in 3-dimensions and higher:

Got it? It's pretty simple, right? Note that when we write (x, y) co-ordinates like ( x y ) , we call ( x y ) a vector. Vector notation can be written horizontally, (e.g. ( x y ) ) or vertically (e.g. ). When using them in 2D transforms, we always add an extra co-ordinate with a number 1 at the end. So (20, 90) would be written ( 20 90 1 ) or like in vector notation.

Multiplicatying a Matrix with a Vector

So, what does the dot product have to do with matrices? Well the idea of the dot product can also be extended to matrices. Let's say you need to multiply the following together:

To do this, you need to produce the dot product of each of the matrix's rows with the vector like this:

A little bit more complicated that the dot product, but not too bad.

So, How Does This Relate To CSS3 Transforms?

A transformation of an block using the matrix() function is done by multiplying the matrix with each of the corner-coordinates of the block which will give the corners of the new object when the transform-origin is set to 0 0. For example let's say you have an HTML element styled like this:

#transformedObject {
    position: absolute;
    left: 0px;
    top: 0px;
    width: 200px;
    height: 80px;
    transform:  matrix(0.9, -0.05, -0.375, 1.375, 220, 20);
    transform-origin: 0 0;
}

What is the end result? Well, first let's take a look at the object without the transform CSS:

When the browser applies the transform to this block, it takes the matrix and multiplies it to each of the corner coordinates. For example, taking the bottom-right corner, (200, 80) or , we get:

Let's look at the results for the other three coordinates. For (200, 0):

For (0, 80) test:

And finally, for (0, 0):

Here is the result:

I don't expect anyone to calculate these by hand on a regular basis (I personally don't). But now you know what these numbers mean. :-)

What About Internet Explorer?

Although transform-origin doesn't work with IE, it is possible to generate the matrix in another browser and use cssSandpaper to generate the shape of the transform in that browser. In order to fix the positioning, one can use Paul Irish's conditional stylesheet fix to position that block manually in IE only (it is what I used on this page so that the matrix transform in my example above appeared correctly in IE). I hope to have transform-origin working in cssSandpaper soon so that it will be unnecessary to do this.

Other Interesting Facts About Matrices

  1. All the other CSS3 transform functions have equivalent matrix notation:

  2. A list of transforms in CSS3 like this:

    #o1 {
       transform-origin: 0px 0px;
       transform: rotate(15deg) translateX(230px) scale(1.5);
    }
    
    

    is the same as multiplying the equivalent matrices together:

    (I know, I didn't tell you how to multiply matrices together. Stephan Waner has written a good tutorial for matrix multiplication if you are interested.)

  3. CSS3 2D-Transforms can only transform blocks into parallelograms. For example, it is impossible to transform a block into this shape:

    In order to do this, one must use CSS3 3D Transforms. This is why the Matrix Construction Set only has three control points to drag around, not four. If you would like to see a version of the Matrix Construction Set to use matrix3d(), please let me know by leaving a comment below.

In Conclusion

I don't expect anyone to be an expert in matrix artithmetic after reading this article. But at least you know what those pesky numbers mean, and this information may become useful in the future.

Acknowledgments

  • The MathML markup used to display the mathematical equations in this article were rendered by MathJax, an excellent open-source JavaScript library
  • The equations on this Physics Forum post was quite helpful in the creation of the Matrix Construction Set.
  • The Matrix Construction Set uses the Sylvester JavaScript library written by James Coglan for performing matrix calculations. It also used my own DragDropHelpers library to smooth out the different browser implementations of HTML5 Drag and Drop.
  • A blog post that has a bunch of math equations on it may be scary for web designers. My apologies