Wed, 11 Apr 2018 21:32:27 +0200
[css-transforms-1] Clarify difference between post-multiply, pre-multiply and multiply. Clean up examples and editorial changes. #909
css-transforms-1/Overview.bs | file | annotate | diff | comparison | revisions |
1.1 --- a/css-transforms-1/Overview.bs Wed Apr 11 17:25:51 2018 +0200 1.2 +++ b/css-transforms-1/Overview.bs Wed Apr 11 21:32:27 2018 +0200 1.3 @@ -145,6 +145,14 @@ 1.4 : <dfn>identity transform function</dfn> 1.5 :: A <a href="#transform-functions">transform function</a> that is equivalent to a identity 4x4 matrix (see <a href="#mathematical-description">Mathematical Description of Transform Functions</a>). Examples for identity transform functions are ''translate(0)'', ''translateX(0)'', ''translateY(0)'', ''scale(1)'', ''scaleX(1)'', ''scaleY(1)'', ''rotate(0)'', ''skew(0, 0)'', ''skewX(0)'', ''skewY(0)'' and ''matrix(1, 0, 0, 1, 0, 0)''. 1.6 1.7 +: <dfn>post-multiply</dfn> 1.8 +:: Term <var>A</var> post-multiplied by term <var>B</var> is equal to <var>A</var> · <var>B</var>. 1.9 + 1.10 +: <dfn>pre-multiply</dfn> 1.11 +:: Term <var>A</var> pre-multiplied by term <var>B</var> is equal to <var>B</var> · <var>A</var>. 1.12 + 1.13 +: <dfn>multiply</dfn> 1.14 +:: Multiply term <var>A</var> by term <var>B</var> is equal to <var>A</var> · <var>B</var>. 1.15 1.16 The Transform Rendering Model {#transform-rendering} 1.17 ==================================================== 1.18 @@ -156,22 +164,23 @@ 1.19 The coordinate space is a coordinate system with two axes: the X axis increases horizontally to the right; the Y axis increases vertically downwards. 1.20 1.21 <p id="transformation-matrix-computation"> 1.22 - The [=transformation matrix=] is computed from the 'transform' and 'transform-origin' properties as follows: 1.23 +The [=transformation matrix=] is computed from the 'transform' and 'transform-origin' properties as follows: 1.24 1.25 - 1. Start with the identity matrix. 1.26 - 2. Translate by the computed X and Y of 'transform-origin' 1.27 - 3. Multiply by each of the transform functions in 'transform' property from left to right 1.28 - 4. Translate by the negated computed X and Y values of 'transform-origin' 1.29 +1. Start with the identity matrix. 1.30 +2. Translate by the computed X and Y of 'transform-origin' 1.31 +3. Multiply by each of the transform functions in 'transform' property from left to right 1.32 +4. Translate by the negated computed X and Y values of 'transform-origin' 1.33 1.34 Transforms apply to [=transformable elements=]. 1.35 1.36 Note: Transformations do affect the visual rendering, but have no affect on the CSS layout other than affecting overflow. Transforms are also taken into account when computing client rectangles exposed via the Element Interface Extensions, namely <a href="https://www.w3.org/TR/cssom-view/#dom-element-getclientrects">getClientRects()</a> and <a href="https://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect">getBoundingClientRect()</a>, which are specified in [[CSSOM-VIEW]]. 1.37 1.38 <div class="example"> 1.39 - <pre> 1.40 -div { 1.41 - transform: translate(100px, 100px); 1.42 -}</pre> 1.43 + <pre><code highlight=css> 1.44 + div { 1.45 + transform: translate(100px, 100px); 1.46 + } 1.47 + </code></pre> 1.48 1.49 This transform moves the element by 100 pixels in both the X and Y directions. 1.50 1.51 @@ -182,13 +191,13 @@ 1.52 </div> 1.53 1.54 <div class="example"> 1.55 - <pre> 1.56 + <pre><code highlight=css> 1.57 div { 1.58 height: 100px; width: 100px; 1.59 transform-origin: 50px 50px; 1.60 transform: rotate(45deg); 1.61 } 1.62 - </pre> 1.63 + </code></pre> 1.64 1.65 The 'transform-origin' property moves the point of origin by 50 pixels in both the X and Y directions. The transform rotates the element clockwise by 45° about the point of origin. After all transform functions were applied, the translation of the origin gets translated back by -50 pixels in both the X and Y directions. 1.66 1.67 @@ -198,28 +207,35 @@ 1.68 </div> 1.69 1.70 <div class="example"> 1.71 - <pre> 1.72 + <pre><code highlight=css> 1.73 div { 1.74 height: 100px; width: 100px; 1.75 transform: translate(80px, 80px) scale(1.5, 1.5) rotate(45deg); 1.76 } 1.77 - </pre> 1.78 + </code></pre> 1.79 1.80 -This transformation translates the local coordinate system by 80 pixels in both the X and Y directions, then applies a 150% scale, then a 45° clockwise rotation about the Z axis. The impact on the rendering of the element can be intepreted as an application of these transforms in reverse order: the elements is rotated, then scaled, then translated. 1.81 +The visual appareance is as if the <a element>div</a> element gets translated by 80px to the bottom left direction, then scaled up by 150% and finally rotated by 45°. 1.82 1.83 - <div class="figure"> 1.84 - <img src="examples/compound_transform.svg" alt="The transform specified above" width="270" height="270"> 1.85 - </div> 1.86 +Each <<transform-function>> can get represented by a corresponding 4x4 matrix. To map a point from the coordinate space of the <a element>div</a> box to the coordinate space of the parent element, these transforms get multiplied in the reverse order: 1.87 +1. The rotation matrix gets multiplied with the scale matrix. 1.88 +2. The result of the previous multiplication is then multiplied with the translation matrix to create the accumulated transformation matrix. 1.89 +3. Finally, the point to map gets pre-multiplied with the accumulated transformation matrix. 1.90 1.91 -Note that an identical rendering can be obtained by nesting elements with the equivalent transforms: 1.92 +For more details see <a href="#transform-function-lists">The Transform Function Lists</a>. 1.93 1.94 - <pre> 1.95 +<div class="figure"> 1.96 + <img src="examples/compound_transform.svg" alt="The transform specified above" width="270" height="270"> 1.97 +</div> 1.98 + 1.99 +Note: The identical rendering can be obtained by nesting elements with the equivalent transforms: 1.100 + 1.101 + <pre><code highlight=html> 1.102 <div style="transform: translate(80px, 80px)"> 1.103 <div style="transform: scale(1.5, 1.5)"> 1.104 <div style="transform: rotate(45deg)"></div> 1.105 </div> 1.106 </div> 1.107 - </pre> 1.108 + </code></pre> 1.109 </div> 1.110 1.111 For elements whose layout is governed by the CSS box model, the transform property does not affect the flow of the content surrounding the transformed element. However, the extent of the overflow area takes into account transformed elements. This behavior is similar to what happens when elements are offset via relative positioning. Therefore, if the value of the 'overflow' property is ''overflow/scroll'' or ''overflow/auto'', scrollbars will appear as needed to see content that is transformed outside the visible area. Specifically, transforms can extend (but do not shrink) the size of the overflow area, which is computed as the union of the bounds of the elements before and after the application of transforms. 1.112 @@ -312,11 +328,11 @@ 1.113 </dl> 1.114 1.115 For SVG elements without associated CSS layout box the initial [=used value=] is ''0 0'' as if the user agent style sheet contained: 1.116 -<pre> 1.117 +<pre><code highlight=css> 1.118 *:not(svg), *:not(foreignObject) > svg { 1.119 transform-origin: 0 0; 1.120 } 1.121 -</pre> 1.122 +</code></pre> 1.123 1.124 The 'transform-origin' property is a <a>resolved value special case</a> property like 'height'. [[!CSSOM]] 1.125 1.126 @@ -378,7 +394,7 @@ 1.127 1.128 This example shows the combination of the 'transform' style property and the <a element-attr for>transform</a> presentation attribute. 1.129 1.130 - <pre> 1.131 + <pre><code highlight=xml> 1.132 <svg xmlns="http://www.w3.org/2000/svg"> 1.133 <style> 1.134 .container { 1.135 @@ -390,7 +406,7 @@ 1.136 <rect width="100" height="100" fill="blue" /> 1.137 </g> 1.138 </svg> 1.139 - </pre> 1.140 + </code></pre> 1.141 1.142 <div class="figure"> 1.143 <img src="examples/svg-translate1.svg" width="470" height="240" alt="Translated SVG container element."> 1.144 @@ -463,7 +479,7 @@ 1.145 1.146 The 'transform-origin' property on the pattern in the following example specifies a ''50%'' translation of the origin in the horizontal and vertical dimension. The 'transform' property specifies a translation as well, but in absolute lengths. 1.147 1.148 - <pre> 1.149 + <pre><code highlight=xml> 1.150 <svg xmlns="http://www.w3.org/2000/svg"> 1.151 <style> 1.152 pattern { 1.153 @@ -480,7 +496,7 @@ 1.154 1.155 <rect width="200" height="200" fill="url(#pattern-1)" /> 1.156 </svg> 1.157 - </pre> 1.158 + </code></pre> 1.159 1.160 An SVG <{pattern}> element doesn't have a bounding box. The [=reference box=] of the referencing <{rect}> element is used instead to solve the relative values of the 'transform-origin' property. Therefore the point of origin will get translated by 100 pixels temporarily to rotate the user space of the <{pattern}> elements content. 1.161 1.162 @@ -507,7 +523,7 @@ 1.163 1.164 The animation effect is post-multiplied to the underlying value for additive <{animate}> animations (see below) instead of added to the underlying value, due to the specific behavior of <<transform-list>> animations. 1.165 1.166 -Issue(w3c/csswg-drafts#909) Clarify post-/pre-multiply column-/row-major order. 1.167 +Issue(w3c/csswg-drafts#909): Clarify post-/pre-multiply column-/row-major order. 1.168 1.169 <var ignore=''>From-to</var>, <var ignore=''>from-by</var> and <var ignore=''>by</var> animations are defined in SMIL to be equivalent to a corresponding <var>values</var> animation. However, <var ignore=''>to</var> animations are a mixture of additive and non-additive behavior [[SMIL3]]. 1.170 1.171 @@ -557,16 +573,16 @@ 1.172 1.173 <div class="example"> 1.174 1.175 -A <var>by</var> animation with a by value v<sub>b</sub> is equivalent to the same animation with a values list with 2 values, the neutral element for addition for the domain of the target attribute (denoted 0) and v<sub>b</sub>, and ''additive="sum"''. [[SMIL3]] 1.176 +A <var>by</var> animation with a by value v<sub>b</sub> is equivalent to the same animation with a values list with 2 values, the neutral element for addition for the domain of the target attribute (denoted 0) and v<sub>b</sub>, and ''additive="sum"''. [[SMIL3]] 1.177 1.178 - <pre> 1.179 - <rect width="100" height="100"> 1.180 +<pre><code highlight=xml> 1.181 +<rect width="100" height="100"> 1.182 <animateTransform attributeName="transform" attributeType="XML" 1.183 - type="scale" by="1" dur="5s" fill="freeze"/> 1.184 - </rect> 1.185 - </pre> 1.186 + type="scale" by="1" dur="5s" fill="freeze"/> 1.187 +</rect> 1.188 +</code></pre> 1.189 1.190 -The neutral element for addition when performing a <var>by</var> animation with ''type="scale"'' is the value 0. Thus, performing the animation of the example above causes the rectangle to be invisible at time 0s (since the animated transform list value is ''scale(0)''), and be scaled back to its original size at time 5s (since the animated transform list value is ''scale(1)''). 1.191 +The neutral element for addition when performing a <var>by</var> animation with ''type="scale"'' is the value 0. Thus, performing the animation of the example above causes the rectangle to be invisible at time 0s (since the animated transform list value is ''scale(0)''), and be scaled back to its original size at time 5s (since the animated transform list value is ''scale(1)''). 1.192 1.193 </div> 1.194 1.195 @@ -579,12 +595,14 @@ 1.196 1.197 In this example the gradient transformation of the linear gradient gets animated. 1.198 1.199 - <pre><linearGradient gradientTransform="scale(2)"> 1.200 -<animate attributeName="gradientTransform" from="scale(2)" to="scale(4)" 1.201 -dur="3s" additive="sum"/> 1.202 -<animate attributeName="transform" from="translate(0, 0)" to="translate(100px, 100px)" 1.203 -dur="3s" additive="sum"/> 1.204 -</linearGradient></pre> 1.205 +<pre><code highlight=xml> 1.206 +<linearGradient gradientTransform="scale(2)"> 1.207 + <animate attributeName="gradientTransform" from="scale(2)" to="scale(4)" 1.208 + dur="3s" additive="sum"/> 1.209 + <animate attributeName="transform" from="translate(0, 0)" to="translate(100px, 100px)" 1.210 + dur="3s" additive="sum"/> 1.211 +</linearGradient> 1.212 +</code></pre> 1.213 1.214 The <{linearGradient}> element specifies the <{linearGradient/gradientTransform}> presentation attribute. The two <{animate}> elements address the target attribute <{linearGradient/gradientTransform}> and 'transform'. Even so all animations apply to the same gradient transformation by taking the value of the <{linearGradient/gradientTransform}> presentation attribute, applying the scaling of the first animation and applying the translation of the second animation one after the other. 1.215 1.216 @@ -666,24 +684,26 @@ 1.217 1.218 If a list of <<transform-function>> is provided, then the net effect is as if each transform function had been specified separately in the order provided. For example, 1.219 1.220 -<pre> 1.221 -<div style="transform:translate(-10px,-20px) scale(2) rotate(45deg) translate(5px,10px)"/> 1.222 -</pre> 1.223 +<pre><code highlight=html> 1.224 +<div style="transform: translate(-10px,-20px) scale(2) rotate(45deg) translate(5px,10px)"/> 1.225 +</code></pre> 1.226 1.227 is functionally equivalent to: 1.228 1.229 -<pre> 1.230 -<div style="transform:translate(-10px,-20px)"> 1.231 - <div style="transform:scale(2)"> 1.232 - <div style="transform:rotate(45deg)"> 1.233 - <div style="transform:translate(5px,10px)"> 1.234 +<pre><code highlight=html> 1.235 +<div style="transform: translate(-10px,-20px)" id="root"> 1.236 + <div style="transform: scale(2)"> 1.237 + <div style="transform: rotate(45deg)"> 1.238 + <div style="transform: translate(5px,10px)" id="child"> 1.239 </div> 1.240 </div> 1.241 </div> 1.242 </div> 1.243 -</pre> 1.244 +</code></pre> 1.245 1.246 -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. 1.247 +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 top ancestor (with the id <code>root</code>) to the deepest descendant (with the id <code>child</code>). The resulting transform is the matrix multiplication of the list of transforms. 1.248 + 1.249 +Issue(w3c/csswg-drafts#909): Backport point mapping from one coordinate space to another from SVG. 1.250 1.251 If a transform function causes the [=current transformation matrix=] of an object to be non-invertible, the object and its content do not get displayed. 1.252 1.253 @@ -691,7 +711,7 @@ 1.254 1.255 The object in the following example gets scaled by 0. 1.256 1.257 - <pre> 1.258 + <pre><code highlight=html> 1.259 <style> 1.260 .box { 1.261 transform: scale(0); 1.262 @@ -701,7 +721,7 @@ 1.263 <div class="box"> 1.264 Not visible 1.265 </div> 1.266 - </pre> 1.267 + </code></pre> 1.268 1.269 The scaling causes a non-invertible CTM for the coordinate space of the div box. Therefore neither the div box, nor the text in it get displayed. 1.270 1.271 @@ -772,7 +792,7 @@ 1.272 The following example describes a transition from ''translateX(100px)'' to ''translateY(100px)'' in 3 seconds on hovering over the div box. Both transform functions derive from the same primitive ''translate()'' 1.273 and therefore can be interpolated. 1.274 1.275 - <pre> 1.276 + <pre><code highlight=css> 1.277 div { 1.278 transform: translateX(100px); 1.279 } 1.280 @@ -781,7 +801,7 @@ 1.281 transform: translateY(100px); 1.282 transition: transform 3s; 1.283 } 1.284 - </pre> 1.285 + </code></pre> 1.286 1.287 For the time of the transition both transform functions get transformed to the common primitive. ''translateX(100px)'' gets converted to ''translate(100px, 0)'' and ''translateY(100px)'' gets converted to ''translate(0, 100px)''. Both transform functions can then get interpolated numerically. 1.288 </div> 1.289 @@ -792,7 +812,7 @@ 1.290 1.291 In this example a two-dimensional transform function gets animated to a three-dimensional transform function. The common primitive is ''translate3d()''. 1.292 1.293 - <pre> 1.294 + <pre><code highlight=css> 1.295 div { 1.296 transform: translateX(100px); 1.297 } 1.298 @@ -801,7 +821,7 @@ 1.299 transform: translateZ(100px); 1.300 transition: transform 3s; 1.301 } 1.302 - </pre> 1.303 + </code></pre> 1.304 1.305 First ''translateX(100px)'' gets converted to ''translate3d(100px, 0, 0)'' and ''translateZ(100px)'' to ''translate3d(0, 0, 100px)'' respectively. Then both converted transform functions get interpolated numerically. 1.306 1.307 @@ -813,27 +833,27 @@ 1.308 1.309 When interpolating between two matrices, each matrix is decomposed into the corresponding translation, rotation, scale, skew. Each corresponding component of the decomposed matrices gets interpolated numerically and recomposed back to a matrix in a final step. 1.310 1.311 +<div class="example"> 1.312 In the following example the element gets translated by 100 pixel in both the X and Y directions and rotated by 1170° on hovering. The initial transformation is 45°. With the usage of transition, an author might expect a animated, clockwise rotation by three and a quarter turns (1170°). 1.313 1.314 -<div class="example"> 1.315 - <pre> 1.316 - <style> 1.317 - div { 1.318 - transform: rotate(45deg); 1.319 - } 1.320 - div:hover { 1.321 - transform: translate(100px, 100px) rotate(1215deg); 1.322 - transition: transform 3s; 1.323 - } 1.324 - </style> 1.325 +<pre><code highlight=html> 1.326 +<style> 1.327 +div { 1.328 + transform: rotate(45deg); 1.329 +} 1.330 +div:hover { 1.331 + transform: translate(100px, 100px) rotate(1215deg); 1.332 + transition: transform 3s; 1.333 +} 1.334 +</style> 1.335 1.336 - <div></div> 1.337 - </pre> 1.338 -</div> 1.339 +<div></div> 1.340 +</code></pre> 1.341 1.342 The number of transform functions on the source transform ''rotate(45deg)'' differs from the number of transform functions on the destination transform ''translate(100px, 100px) rotate(1125deg)''. According to the last rule of <a href="#interpolation-of-transforms">Interpolation of Transforms</a>, both transforms must be interpolated by matrix interpolation. With converting the transformation functions to matrices, the information about the three turns gets lost and the element gets rotated by just a quarter turn (90°). 1.343 1.344 To achieve the three and a quarter turns for the example above, source and destination transforms must fulfill the third rule of <a href="#interpolation-of-transforms">Interpolation of Transforms</a>. Source transform could look like ''translate(0, 0) rotate(45deg)'' for a linear interpolation of the transform functions. 1.345 +</div> 1.346 1.347 In the following we differ between the <a href="#interpolation-of-2d-matrices">interpolation of two 2D matrices</a> and the interpolation of two matrices where at least one matrix is not a [=2D matrix=]. 1.348