Pragmatic CSS3
-webit-transform matrix3d
Tutorial

JavaScript JS Documentation: JS Function arity, JavaScript Function arity, JS Function .arity, JavaScript Function .arity

Matrix3d

When you dive into apples documentation of matrix3d you will find a short definition "Specifies a 3D transformation as a 4 x 4 matrix." following by the function definition in code:

matrix3d(m00, m01, m02, m03,
         m10, m11, m12, m13,
         m20, m21, m22, m23,
         m30, m31, m31, m33)

Unless you're a math god you probably think to yourself: "I find the lack of documentation quite disturbing" followed by the question on how to build the really hot Superman stuff on left. The described approach is not meant to be mathematical or complete - I just want to fill a little documentation gap.

Some Linear Algebra

Every complex transformation could be seperated into three basic transformations:

Since this works vice versa these three basic steps could be composed into a giant omnipotent transformation matrix. So far so good, but how do I get from my basic steps to that wild parameter list that matrix3d needs? Let's start with the most simple matrix we have in mathmatics (for making my life easier I'm using Sylvester for matrix math calculations).

The Identity Matrix

identityMatrix = $M([
  [1,0,0,0],
  [0,1,0,0],
  [0,0,1,0],
  [0,0,0,1]
])

This matrix actually does nothing! Nil! Null! Nada! No pixels get harmed! I divided this matrix in two sections. The red section is the area were you will describe the Rotation and Scale. The yellow area is were you describe the Translation. The other parameters are rarely used in real life except for really weird LSD style FX demos.

We start with creating a scale matrix by multiplying the identity matrix with a scaler (a number). scaleMatrix = indentityMatrix.multiply(s)

The Scale Matrix

scaleMatrix = $M([
  [s,0,0,0],
  [0,s,0,0],
  [0,0,s,0],
  [0,0,0,s]
])

Since we don't want to transform the translation coordinates we rip off the last scale and replace it with 1:

scaleMatrix = $M([
  [s,0,0,0],
  [0,s,0,0],
  [0,0,s,0],
  [0,0,0,1]
])

The Rotation Matrixes

Rotation can happen around three Axis X,Y,Z let's say around the degree values a,b,c. The corresponding matrixes to describe this transformation are:

rotationXMatrix = $M([
  [1,0,0,0],
  [0,Math.cos(a), Math.sin(-a), 0],
  [0,Math.sin(a), Math.cos( a), 0],
  [0,0,0,1]
])

rotationYMatrix = $M([
  [Math.cos( b), 0, Math.sin(b),0],
  [0,1,0,0],
  [Math.sin(-b), 0, Math.cos(b), 0],
  [0,0,0,1]
])

rotationZMatrix = $M([
  [Math.cos(c), Math.sin(-c), 0, 0],
  [Math.sin(c), Math.cos( c), 0, 0],
  [0,0,1,0],
  [0,0,0,1]
])

Each matrix describes a rotation around one axis.

The Translation Matrix

translationMatrix = $M([
  [1,0,0,0],
  [0,1,0,0],
  [0,0,1,0],
  [tx,ty,tz,1]
])

The translation matrix leaves the most pixels but add the values tx, ty and tz to the final vector result.

The fun part

Yes math can be funny. Each of this matrix can be used in a composition. So if I want to rotate something around all axis and then translate it some pixels then you just multiply the matrixes and that's it:

tM = rotationXMatrix
      .x(rotationYMatrix)
      .x(rotationZMatrix)
      .x(scaleMatrix)
      .x(translationMatrix)

Finally you apply it to the image:

s  = "matrix3d("
s += tM.e(1,1).toFixed(10) + "," + tM.e(1,2).toFixed(10) + "," + tM.e(1,3).toFixed(10) + "," + tM.e(1,4).toFixed(10) + ","
s += tM.e(2,1).toFixed(10) + "," + tM.e(2,2).toFixed(10) + "," + tM.e(2,3).toFixed(10) + "," + tM.e(2,4).toFixed(10) + ","
s += tM.e(3,1).toFixed(10) + "," + tM.e(3,2).toFixed(10) + "," + tM.e(3,3).toFixed(10) + "," + tM.e(3,4).toFixed(10) + ","
s += tM.e(4,1).toFixed(10) + "," + tM.e(4,2).toFixed(10) + "," + tM.e(4,3).toFixed(10) + "," + tM.e(4,4).toFixed(10)
s += ")"

document.getElementById('darth-vader').style['-webkit-transform'] = s

Caveats

The first thing is - if you google for linear transformations and you find examples of those matrixes and you wonder why they differ a little from the webkit matrix. The webkit matrix is transponed - so simply transpone it and it should work. The second thing is that CSS does not allow scientific numbers (like 123e-15) as parameters - so you have to use toFixed(numberOfDigits) to normalize them.

Env

The demo just runs on webkit browsers like chrome or safari. The sourcecode is written in coffeescript which is way cooler than javascript - but the compiled javascript should be readable aswell. You can grab the full tutorial + source on github. If you're interested in cutting webdevelopment you might follow @9elements or become a facebook fan. If you have further questions drop me a line on twitter @sippndipp (you may follow me aswell.)

Twitter it

or push it on Hackernews

Credits