CSS 2D Transforms allows elements rendered by CSS to be transformed in two-dimensional space.
The list of changes made to this specification is available.
This section is not normative.
The CSS visual formatting model describes a coordinate system within which each element is positioned. Positions and sizes in this coordinate space can be thought of as being expressed in pixels, starting in the upper left corner of the parent with positive values proceeding to the right and down.
This coordinate space can be modified with the 'transform' property. Using transform, elements can be translated, rotated and scaled in two dimensional space. The coordinate space behaves as described in the coordinate system transformations section of the SVG 1.1 specification. This is a coordinate system with two axes: the X axis increases horizontally to the right; the Y axis increases vertically downwards.
Transforms apply to block-level and atomic inline-level elements, but do not apply to elements which may be split into multiple inline-level boxes.
Specifying a value other than 'none' for the 'transform' property establishes a new local coordinate system at the element that it is applied to. Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent. From the perspective of the user, an element effectively accumulates all the 'transform' properties of its ancestors as well as any local transform applied to it. The accumulation of these transforms defines a current transformation matrix (CTM) for the element.
The transform property does not affect the flow of the content surrounding the transformed element. However, the value of the overflow area takes into account transformed elements. This behavior is similar to what happens when elements are translated via relative positioning. Therefore, if the value of the 'overflow' property is 'scroll' or 'auto', scrollbars will appear as needed to see content that is transformed outside the visible area.
Any value other than 'none' for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.
A two-dimensional transformation is applied to the coordinate system an element renders in through the 'transform' property. This property contains a list of transform functions. The final transformation value for a coordinate system is obtained by converting each function in the list to its corresponding matrix (either defined in this specification or by reference to the SVG specification), then multiplying the matrices.
| Name: | transform |
| Value: | none | <transform-function> [ <transform-function> ]* |
| Initial: | none |
| Applies to: | block-level and atomic inline-level elements |
| Inherited: | no |
| Percentages: | refer to the size of the element's border box |
| Media: | visual |
| Computed value: | Same as specified value. |
The 'transform-origin' property establishes the origin of transformation for a coordinate system. This property is applied by first translating the element's coordinate system by the negated value of the property, then applying the local transform, then translating by the property value. This effectively moves the desired transformation origin of the element to (0,0) in the local coordinate system, then applies the local transform, then moves the element back to its original position.
If only one value is specified, the second value is assumed to be 'center'. If two values are given and at least one value is not a keyword, then the first value represents the horizontal position (or offset) and the second represents the vertical position (or offset). <percentage> and <length> values here represent an offset of the transform origin from the top left corner of the element's border box.
If three or four values are given, then each <percentage> or<length> represents an offset and must be preceded by a keyword, which specifies from which edge the offset is given. For example, ''transform-origin: bottom 10px right 20px'' represents a ''10px'' vertical offset up from the bottom edge and a ''20px'' horizontal offset leftward from the right edge. If three values are given, the missing offset is assumed to be zero.
Positive values represent an offset inward from the edge of the border box. Negative values represent an offset outward from the edge of the border box.
| Name: | transform-origin |
| Value: |
[ top | bottom ] | [ <percentage> | <length> | left | center | right ] [ <percentage> | <length> | top | center | bottom ]? | [ center | [ left | right ] [ <percentage> | <length> ]? ] && [ center | [ top | bottom ] [ <percentage> | <length> ]? ] |
| Initial: | 50% 50% |
| Applies to: | block-level and atomic inline-level elements |
| Inherited: | no |
| Percentages: | refer to the size of the element's border box |
| Media: | visual |
| Computed value: | For <length> the absolute value, otherwise a percentage |
The value of the transform property is a list of <transform-functions> applied in the order provided. The individual transform functions are separated by whitespace. The set of allowed transform functions is given below. In this list the type <translation-value> is defined as a <length> or <percentage> value, and the <angle> type is defined by CSS Values and Units.
The <translation-value> values are defined as [<percentage> | <length>]. All other value types are described as CSS types. If a list of transforms is provided, then the net effect is as if each transform had been specified separately in the order provided. For example,
<div style="transform:translate(-10px,-20px) scale(2) rotate(45deg) translate(5px,10px)"/>
is functionally equivalent to:
<div style="transform:translate(-10px,-20px)">
<div style="transform:scale(2)">
<div style="transform:rotate(45deg)">
<div style="transform:translate(5px,10px)">
</div>
</div>
</div>
</div>
That is, in the absence of other styling that affects position and dimensions, a nested set of transforms is equivalent to a single list of transform functions, applied from the outside in. The resulting transform is the matrix multiplication of the list of transforms.
div {
transform: translate(100px, 100px);
}
Move the element by 100 pixels in both the X and Y directions.
div {
height: 100px; width: 100px;
transform: translate(80px, 80px) scale(1.5, 1.5) rotate(45deg);
}
Move the element by 80 pixels in both the X and Y directions, then scale the element by 150%, then rotate it 45 degrees clockwise about the Z axis. Note that the scale and rotate operate about the center of the element, since the element has the default transform-origin of 50% 50%.
When animating or transitioning the value of a transform property the rules described below are applied. The 'from' transform is the transform at the start of the transition or current keyframe. The 'end' transform is the transform at the end of the transition or current keyframe.
For example, if the 'from' transform is "scale(2)" and the 'to' transform is "none" then the value "scale(1)" will be used as the 'to' value, and animation will proceed using the rule above. Similarly, if the 'from' transform is "none" and the 'to' transform is "scale(2) rotate(50deg)" then the animation will execute as if the 'from' value is "scale(1) rotate(0)".
The identity functions are translate(0), translateX(0), translateY(0), scale(1), scaleX(1), scaleY(1), rotate(0), skewX(0), skewY(0) and matrix(1, 0, 0, 1, 0, 0).
In some cases, an animation might cause a transformation matrix to be singular or non-invertible. For example, an animation in which scale moves from 1 to -1. At the time when the matrix is in such a state, the transformed element is not rendered.
When interpolating between 2 matrices, each is decomposed into the corresponding translation, rotation, scale, skew, and perspective values. Not all matrices can be accurately described by these values. Those that can't are decomposed into the most accurate representation possible, using the technique below. This technique is taken from The "unmatrix" method in "Graphics Gems II, edited by Jim Arvo". The pseudocode below works on a 4x4 homogeneous matrix. A 3x2 2D matrix is therefore first converted to 4x4 homogeneous form.
The pseudocode here is written with column-major matrices, as would be used in C/C++, but the SVG spec illustrates matrixes using row-major notation. Change this pseudocode to avoid confusion?
It's also unfortunate that we have to include math for 4x4 matrices into the 2D transforms spec. Find a way to avoid that?
Input: matrix2d ; a 3x2 transformation matrix
; rotation matrix in m11,m12,m21,m22
; x,y translation in m31,m32
Output: matrix ; a 4x4 homgeneous matrix
matrix[0][0] = matrix2d[0][0]
matrix[0][1] = matrix2d[0][1]
matrix[0][2] = 0
matrix[0][3] = 0
matrix[1][0] = matrix2d[1][0]
matrix[1][1] = matrix2d[1][1]
matrix[1][2] = 0
matrix[1][3] = 0
matrix[2][0] = 0
matrix[2][1] = 0
matrix[2][2] = 1
matrix[2][3] = 0
matrix[3][0] = matrix2d[2][0]
matrix[3][1] = matrix2d[2][1]
matrix[3][2] = 0
matrix[3][3] = 1
return matrix
Input: matrix ; a 4x4 matrix
Output: translate ; a 3 component vector
rotate ; Euler angles, represented as a 3 component vector
scale ; a 3 component vector
skew ; skew factors XY,XZ,YZ represented as a 3 component vector
perspective ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can
Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix):
float determinant(matrix) returns the 4x4 determinant of the matrix
matrix inverse(matrix) returns the inverse of the passed matrix
matrix transpose(matrix) returns the transpose of the passed matrix
point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix
and returns the transformed point
float length(point) returns the length of the passed vector
point normalize(point) normalizes the length of the passed point to 1
float dot(point, point) returns the dot product of the passed points
float cos(float) returns the cosine of the passed angle in radians
float asin(float) returns the arcsine in radians of the passed value
float atan2(float y, float x) returns the principal value of the arc tangent of
y/x, using the signs of both arguments to determine
the quadrant of the return value
Decomposition also makes use of the following function:
point combine(point a, point b, float ascl, float bscl)
result[0] = (ascl * a[0]) + (bscl * b[0])
result[1] = (ascl * a[1]) + (bscl * b[1])
result[2] = (ascl * a[2]) + (bscl * b[2])
return result
// Normalize the matrix.
if (matrix[3][3] == 0)
return false
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
matrix[i][j] /= matrix[3][3]
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix
for (i = 0; i < 3; i++)
perspectiveMatrix[i][3] = 0
perspectiveMatrix[3][3] = 1
if (determinant(perspectiveMatrix) == 0)
return false
// First, isolate perspective.
if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
// rightHandSide is the right hand side of the equation.
rightHandSide[0] = matrix[0][3];
rightHandSide[1] = matrix[1][3];
rightHandSide[2] = matrix[2][3];
rightHandSide[3] = matrix[3][3];
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
inversePerspectiveMatrix = inverse(perspectiveMatrix)
transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
// Clear the perspective partition
matrix[0][3] = matrix[1][3] = matrix[2][3] = 0
matrix[3][3] = 1
else
// No perspective.
perspective[0] = perspective[1] = perspective[2] = 0
perspective[3] = 1
// Next take care of translation
translate[0] = matrix[3][0]
matrix[3][0] = 0
translate[1] = matrix[3][1]
matrix[3][1] = 0
translate[2] = matrix[3][2]
matrix[3][2] = 0
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
for (i = 0; i < 3; i++)
row[i][0] = matrix[i][0]
row[i][1] = matrix[i][1]
row[i][2] = matrix[i][2]
// Compute X scale factor and normalize first row.
scale[0] = length(row[0])
row[0] = normalize(row[0])
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = dot(row[0], row[1])
row[1] = combine(row[1], row[0], 1.0, -skew[0])
// Now, compute Y scale and normalize 2nd row.
scale[1] = length(row[1])
row[1] = normalize(row[1])
skew[0] /= scale[1];
// Compute XZ and YZ shears, make 3rd row orthogonal
skew[1] = dot(row[0], row[2])
row[2] = combine(row[2], row[0], 1.0, -skew[1])
skew[2] = dot(row[1], row[2])
row[2] = combine(row[2], row[1], 1.0, -skew[2])
// Next, get Z scale and normalize 3rd row.
scale[2] = length(row[2])
row[2] = normalize(row[2])
skew[1] /= scale[2]
skew[2] /= scale[2]
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = cross(row[1], row[2])
if (dot(row[0], pdum3) < 0)
for (i = 0; i < 3; i++) {
scale[0] *= -1;
row[i][0] *= -1
row[i][1] *= -1
row[i][2] *= -1
// Now, get the rotations out
rotate[1] = asin(-row[0][2]);
if (cos(rotate[1]) != 0)
rotate[0] = atan2(row[1][2], row[2][2]);
rotate[2] = atan2(row[0][1], row[0][0]);
else
rotate[0] = atan2(-row[2][0], row[1][1]);
rotate[2] = 0;
return true;
Once decomposed, each component of each returned value of the source matrix is linearly interpolated with the corresponding component of the destination matrix. For instance, the translate[0], translate[1] and translate[2] values are interpolated numerically, and the result is used to set the translation of the animating element.
This section is not normative.
After interpolation the resulting values are used to position the element. One way to use these values is to recompose them into a 4x4 matrix. This can be done using the Transformation Functions of the transform property. The following JavaScript example produces a string for this purpose. The values passed in are the output of the Unmatrix function above:
function compose(translate, rotate, scale, skew, perspective, elementID)
{
var s = "matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, " +
perspective[0] + ", " + perspective[1] + ", " +
perspective[2] + ", " + perspective[3] + ")";
s += " translate3d(" + translate[0] + ", " + translate[1] + ", " + translate[2] + ")";
s += " rotateX(" + rotate[0] + ")";
s += " rotateY(" + rotate[1] + ")";
s += " rotateZ(" + rotate[2] + ")";
s += " matrix3d(1,0,0,0, 0,1,0,0, 0," + skew[2] + ",1,0, 0,0,0,1)";
s += " matrix3d(1,0,0,0, 0,1,0,0, " + skew[1] + ",0,1,0, 0,0,0,1)";
s += " matrix3d(1,0,0,0, " + skew[0] + ",1,0,0, 0,0,1,0, 0,0,0,1)";
s += " scale3d(" + scale[0] + ", " + scale[1] + ", " + scale[2] + ")";
document.getElementById(elementID).style.transform = s;
}
This section describes the interfaces and functionality added to the DOM to support runtime access to the functionality described above.
The CSSMatrix interface represents a 4x4 homogeneous matrix.
interface CSSMatrix {
attribute float a;
attribute float b;
attribute float c;
attribute float d;
attribute float e;
attribute float f;
void setMatrixValue(in DOMString string) raises(DOMException);
CSSMatrix multiply(in CSSMatrix secondMatrix);
CSSMatrix inverse() raises(DOMException);
CSSMatrix translate(in float x, in float y);
CSSMatrix scale(in float scaleX, in float scaleY);
CSSMatrix skewX(in float angle);
CSSMatrix skewY(in float angle);
CSSMatrix rotate(in float angle);
};
a-f of type float
setMatrixValue
setMatrixValue method replaces the existing matrix with one computed from parsing the passed string
as though it had been assigned to the transform property in a CSS style rule.
string of type DOMString
DOMException SYNTAX_ERR
multiply
multiply method returns a new CSSMatrix which is the result of this matrix multiplied by the passed matrix, with the passed matrix to the right. This matrix is not modified.
secondMatrix of type CSSMatrix
CSSMatrix
inverse
inverse method returns a new matrix which is the inverse of this matrix. This matrix is not modified.
CSSMatrix
DOMException NOT_SUPPORTED_ERR
translate
translate method returns a new matrix which is this matrix multiplied by a translation matrix containing the passed values. This matrix is not modified.
x of type float
y of type float
CSSMatrix
scale
scale method returns a new matrix which is this matrix multiplied by a scale matrix containing the passed values. If the y component is undefined, the x component value is used in its place. This matrix is not modified.
scaleX of type float
scaleY of type float
CSSMatrix
rotate
rotate method returns a new matrix which is this matrix multiplied by a rotation matrix. The rotation value is in degrees. This matrix is not modified.
angle of type float
CSSMatrix
skewX
skewX method returns a new matrix which is this matrix multiplied by a matrix representing a skew along the x-axis. The rotation value is in degrees. This matrix is not modified.
angle of type float
CSSMatrix
skewY
skewX method returns a new matrix which is this matrix multiplied by a matrix representing a skew along the y-axis. The rotation value is in degrees. This matrix is not modified.
angle of type float
CSSMatrix
In addition to the interface listed above, the getComputedStyle method of the Window
object has been updated. The transform property of the style object returned by
getComputedStyle contains a DOMString of the form "matrix(a, b, c, d, e, f)"
representing the 3x2 matrix that is the result of applying the individual functions listed in the
transform property.