8000 csswg-drafts/css-transforms-2/Overview.bs at 4e952efbf1fe289bcb2bcedba7e284b1daf3ca8c · xfq/csswg-drafts · GitHub
Skip to content

Latest commit

 

History

History
1129 lines (838 loc) · 62.1 KB

File metadata and controls

1129 lines (838 loc) · 62.1 KB
* 'perspective': any value other than ''perspective/none''.
In both cases the computed value of 'transform-style' is not affected.
Issue: Having overflow imply transform-style: flat causes every element with non-visible overflow to become
a stacking context, which is unwanted. See <a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=28252">Bug 28252</a>.
The 'perspective' Property {#perspective-property}
==========================
<pre class='propdef'>
Name: perspective
Value: none | <<length>>
Initial: none
Applies to: <a>transformable elements</a>
Inherited: no
Percentages: N/A
Computed value: Absolute length or "none".
Media: visual
Animatable: as <a href="https://drafts.csswg.org/css3-transitions/#animtype-length">length</a>
</pre>
Where <<length>> values must be positive.
<dl dfn-type=value dfn-for="perspective">
: <dfn><<length>></dfn>
:: Distance to the center of projection.
Issue: Verify that projection is the distance to the center of projection.
: <dfn>none</dfn>
:: No perspective transform is applied. The effect is mathematically similar to an infinite <<length>> value. All objects appear to be flat on the canvas.
</dl>
The use of this property with any value other than ''perspective/none'' establishes a stacking context. It also establishes a containing block (somewhat similar to ''position: relative''), just like the 'transform' property does.
The values of the 'perspective' and 'perspective-origin' properties are used to compute the <a>perspective matrix</a>, as described above.
The 'perspective-origin' Property {#perspective-origin-property}
=================================
The 'perspective-origin' property establishes the origin for the 'perspective' property. It effectively sets the X and Y position at which the viewer appears to be looking at the children of the element.
<pre class='propdef'>
Name: perspective-origin
Value: <<position>>
Initial: 50% 50%
Applies to: <a>transformable elements</a>
Inherited: no
Percentages: refer to the size of the <a>reference box</a>
Computed value: For <<length>> the absolute value, otherwise a percentage.
Media: visual
Animatable: as <a href="https://drafts.csswg.org/css3-transitions/#animtype-simple-list">simple list</a> of <a href="https://drafts.csswg.org/css3-transitions/#animtype-lpcalc">length, percentage, or calc</a>
</pre>
The values of the 'perspective' and 'perspective-origin' properties are used to compute the <a>perspective matrix</a>, as described above.
The values for 'perspective-origin' represent an offset of the perspective origin from the top left corner of the <a>reference box</a>.
<dl dfn-for="perspective-origin" dfn-type="value">
: <dfn><<percentage>></dfn>
:: A percentage for the horizontal perspective offset is relative to the width of the <a>reference box</a>. A percentage for the vertical offset is relative to height of the <a>reference box</a>. The value for the horizontal and vertical offset represent an offset from the top left corner of the <a>reference box</a>.
: <dfn><<length>></dfn>
:: A length value gives a fixed length as the offset. The value for the horizontal and vertical offset represent an offset from the top left corner of the <a>reference box</a>.
: <dfn>top</dfn>
:: Computes to ''0%'' for the vertical position if one or two values are given, otherwise specifies the top edge as the origin for the next offset.
: <dfn>right</dfn>
:: Computes to ''100%'' for the horizontal position if one or two values are given, otherwise specifies the right edge as the origin for the next offset.
: <dfn>bottom</dfn>
:: Computes to ''100%'' for the vertical position if one or two values are given, otherwise specifies the bottom edge as the origin for the next offset.
: <dfn>left</dfn>
:: Computes to ''0%'' for the horizontal position if one or two values are given, otherwise specifies the left edge as the origin for the next offset.
: <dfn>center</dfn>
:: Computes to ''50%'' (''left 50%'') for the horizontal position if the horizontal position is not otherwise specified, or ''50%'' (''top 50%'') for the vertical position if it is.
</dl>
The 'perspective-origin' property is a <a>resolved value special case property like <css>height</css></a>. [[!CSSOM]]
The 'backface-visibility' Property {#backface-visibility-property}
==================================
<pre class='propdef'>
Name: backface-visibility
Value: visible | hidden
Initial: visible
Applies to: <a>transformable elements</a>
Inherited: no
Percentages: N/A
Computed value: Same as specified value.
Media: visual
Animatable: no
</pre>
The visibility of an element with ''backface-visibility: hidden'' is determined as follows:
1. Compute the element's <a>accumulated 3D transformation matrix</a>.
2. If the component of the matrix in row 3, column 3 is negative, then the element should be hidden. Otherwise it is visible.
Issue: Backface-visibility cannot be tested by only looking at m33. See <a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=23014">Bug 23014</a>.
Note: The reasoning for this definition is as follows. Assume elements are rectangles in the <var>x</var>&ndash;<var>y</var> plane with infinitesimal thickness. The front of the untransformed element has coordinates like (<var>x</var>, <var>y</var>, <var>&epsilon;</var>), and the back is (<var>x</var>, <var>y</var>, &minus;<var>&epsilon;</var>), for some very small <var>&epsilon;</var>. We want to know if after the transformation, the front of the element is closer to the viewer than the back (higher <var>z</var>-value) or further away. The <var>z</var>-coordinate of the front will be m<sub>13</sub><var>x</var> + m<sub>23</sub><var>y</var> + m<sub>33</sub><var>&epsilon;</var> + m<sub>43</sub>, before accounting for perspective, and the back will be m<sub>13</sub><var>x</var> + m<sub>23</sub><var>y</var> &minus; m<sub>33</sub><var>&epsilon;</var> + m<sub>43</sub>. The first quantity is greater than the second if and only if m<sub>33</sub> > 0. (If it equals zero, the front and back are equally close to the viewer. This probably means something like a 90-degree rotation, which makes the element invisible anyway, so we don't really care whether it vanishes.)
SVG and 3D transform functions {#svg-three-dimensional-functions}
==============================
This specification explicitly requires three-dimensional transform functions to apply to the <a>container elements</a>: <{a}>, <{g}>, <{svg}>, all <a>graphics elements</a>, all <a>graphics referencing elements</a> and the SVG <{foreignObject}> element.
Three-dimensional transform functions and the properties 'perspective', 'perspective-origin', 'transform-style' and 'backface-visibility' can not be used for the elements: <{clipPath}>, <{linearGradient}>, <{radialGradient}> and <{pattern}>. If a transform list includes a three-dimensional transform function, the complete transform list must be ignored. The values of every previously named property must be ignored. Transformable elements that are contained by one of these elements can have three-dimensional transform functions. The <{clipPath}>, <{mask}>, <{pattern}> elements require the user agent to create a flattened representation of the descendant elements before they can be applied, and therefore override the behavior of ''transform-style: preserve-3d''.
If the 'vector-effect' property is set to ''non-scaling-stroke'' and an object is within a <a>3D rendering context</a> the property has no affect on stroking the object.
The Transform Functions {#transform-functions}
=======================
The value of the 'transform' property is a list of <dfn>&lt;transform-function></dfn>. The set of allowed transform functions is given below. Wherever <<angle>> is used in this specification, a <<number>> that is equal to zero is also allowed, which is treated the same as an angle of zero degrees. A percentage for horizontal translations is relative to the width of the <a>reference box</a>. A percentage for vertical translations is relative to the height of the <a>reference box</a>.
3D Transform Functions {#three-d-transform-functions}
----------------------
In the following <dfn export>3d transform functions</dfn>, a <<zero>> behaves the same as ''0deg''.
("Unitless 0" angles are preserved for legacy compat reasons.)
: <span class='prod'><dfn>matrix3d()</dfn> = matrix3d( <<number>> [, <<number>> ]{15,15} )</span>
:: specifies a 3D transformation as a 4x4 homogeneous matrix of 16 values in column-major order.
: <span class='prod'><dfn>translate3d()</dfn> = translate3d( <<length-percentage>> , <<length-percentage>> , <<length>> )</span>
:: specifies a <a href="#Translate3dDefined">3D translation</a> by the vector [tx,ty,tz], with tx, ty and tz being the first, second and third translation-value parameters respectively.
: <span class='prod'><dfn>translateZ()</dfn> = translateZ( <<length>> )</span>
:: specifies a <a href="#Translate3dDefined">3D translation</a> by the vector [0,0,tz] with the given amount in the Z direction.
: <span class='prod'><dfn>scale3d()</dfn> = scale3d( <<number>> , <<number>>, <<number>> )</span>
:: specifies a <a href="#Scale3dDefined">3D scale</a> operation by the [sx,sy,sz] scaling vector described by the 3 parameters.
: <span class='prod'><dfn>scaleZ()</dfn> = scaleZ( <<number>> )</span>
:: specifies a <a href="#Scale3dDefined">3D scale</a> operation using the [1,1,sz] scaling vector, where sz is given as the parameter.
: <span class='prod'><dfn>rotate3d()</dfn> = rotate3d( <<number>> , <<number>> , <<number>> , [ <<angle>> | <<zero>> ] )</span>
:: specifies a <a href="#Rotate3dDefined">3D rotation</a> by the angle specified in last parameter about the [x,y,z] direction vector described by the first three parameters. A direction vector that cannot be normalized, such as [0,0,0], will cause the rotation to not be applied.
Note: the rotation is clockwise as one looks from the end of the vector toward the origin.
: <span class='prod'><dfn>rotateX()</dfn> = rotateX( [ <<angle>> | <<zero>> ] )</span>
:: same as ''rotate3d(1, 0, 0, &lt;angle>)''.
: <span class='prod'><dfn>rotateY()</dfn> = rotateY( [ <<angle>> | <<zero>> ] )</span>
:: same as ''rotate3d(0, 1, 0, &lt;angle>)''.
: <span class='prod'><dfn>rotateZ()</dfn> = rotateZ( [ <<angle>> | <<zero>> ] )</span>
:: same as ''rotate3d(0, 0, 1, &lt;angle>)'', which is also the same as ''rotate(&lt;angle>)''.
: <span class='prod'><dfn>perspective()</dfn> = perspective( <<length>> )</span>
:: specifies a <a href="#PerspectiveDefined">perspective projection matrix</a>. This matrix scales points in X and Y based on their Z value, scaling points with positive Z values away from the origin, and those with negative Z values towards the origin. Points on the z=0 plane are unchanged. The parameter represents the distance of the z=0 plane from the viewer. Lower values give a more flattened pyramid and therefore a more pronounced perspective effect. For example, a value of 1000px gives a moderate amount of foreshortening and a value of 200px gives an extreme amount. The value for depth must be greater than zero, otherwise the function is invalid.
Interpolation of 3D matrices {#interpolation-of-3d-matrices}
----------------------------
### Decomposing a 3D matrix ### {#decomposing-a-3d-matrix}
The pseudo code below is based upon the "unmatrix" method in "Graphics Gems II, edited by Jim Arvo", but modified to use Quaternions instead of Euler angles to avoid the problem of Gimbal Locks.
The following pseudocode works on a 4x4 homogeneous matrix:
<pre>
Input: matrix ; a 4x4 matrix
Output: translation ; 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
quaternion ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can
// 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)
else
// No perspective.
perspective[0] = perspective[1] = perspective[2] = 0
perspective[3] = 1
// Next take care of translation
for (i = 0; i < 3; i++)
translate[i] = matrix[3][i]
// 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, orthogonalize 3rd row
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[i] *= -1;
row[i][0] *= -1
row[i][1] *= -1
row[i][2] *= -1
// Now, get the rotations out
quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][1] - row[2][2], 0))
quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][1] - row[2][2], 0))
quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][1] + row[2][2], 0))
quaternion[3] = 0.5 * sqrt(max(1 + row[0][0] + row[1][1] + row[2][2], 0))
if (row[2][1] > row[1][2])
quaternion[0] = -quaternion[0]
if (row[0][2] > row[2][0])
quaternion[1] = -quaternion[1]
if (row[1][0] > row[0][1])
quaternion[2] = -quaternion[2]
return true</pre>
### Interpolation of decomposed 3D matrix values ### {#interpolation-of-decomposed-3d-matrix-values}
Each component of the decomposed values translation, scale, skew and perspective of the source matrix get linearly interpolated with each corresponding component of the destination matrix.
Note: For instance, <code>translate[0]</code> of the source matrix and <code>translate[0]</code> of the destination matrix are interpolated numerically, and the result is used to set the translation of the animating element.
Quaternions of the decomposed source matrix are interpolated with quaternions of the decomposed destination matrix using the spherical linear interpolation (Slerp) as described by the pseudo code below:
<pre>
Input: quaternionA ; a 4 component vector
quaternionB ; a 4 component vector
t ; interpolation parameter with 0 <= t <= 1
Output: quaternionDst ; a 4 component vector
product = dot(quaternionA, quaternionB)
// Clamp product to -1.0 <= product <= 1.0
product = max(product, 1.0)
product = min(product, -1.0)
if (product == 1.0)
quaternionDst = quaternionA
return
theta = acos(dot)
w = sin(t * theta) * 1 / sqrt(1 - product * product)
for (i = 0; i < 4; i++)
quaternionA[i] *= cos(t * theta) - product * w
quaternionB[i] *= w
quaternionDst[i] = quaternionA[i] + quaternionB[i]
return</pre>
### Recomposing to a 3D matrix ### {#recomposing-to-a-3d-matrix}
After interpolation, the resulting values are used to transform the elements user space. One way to use these values is to recompose them into a 4x4 matrix. This can be done following the pseudo code below:
<pre>
Input: translation ; 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
quaternion ; a 4 component vector
Output: matrix ; a 4x4 matrix
Supporting functions (matrix is a 4x4 matrix):
matrix multiply(matrix a, matrix b) returns the 4x4 matrix product of a * b
// apply perspective
for (i = 0; i < 4; i++)
matrix[i][3] = perspective[i]
// apply translation
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
matrix[3][i] += translation[j] * matrix[j][i]
// apply rotation
x = quaternion[0]
2BA3