Mon, 25 Mar 2013 12:06:12 -0700
[css3-transforms] Fix syntax of transform-origin/perspective-origin properties.
bert@5324 | 1 | <!DOCTYPE html public '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'> |
vhardy@3672 | 2 | <html lang="en"> |
vhardy@3672 | 3 | <head profile="http://www.w3.org/2006/03/hcard"> |
bert@6704 | 4 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > |
vhardy@3719 | 5 | <title>CSS Transforms</title> |
bert@6704 | 6 | <link rel="stylesheet" type="text/css" href="../default.css" > |
dschulze@5541 | 7 | <script src='http://test.csswg.org/harness/annotate.js#CSS3-TRANSFORMS_DEV' type='text/javascript' defer></script> |
vhardy@3672 | 8 | |
vhardy@3732 | 9 | <style type="text/css"> |
simon@4376 | 10 | .term { |
simon@4376 | 11 | font-style: italic; |
simon@4376 | 12 | } |
vhardy@3672 | 13 | </style> |
bert@6704 | 14 | <link rel="stylesheet" type="text/css" href="http://www.w3.org/StyleSheets/TR/W3C-ED.css" > |
vhardy@3672 | 15 | |
vhardy@3672 | 16 | </head> |
vhardy@3672 | 17 | <body> |
vhardy@3672 | 18 | <div id="div-head" class="head"> |
vhardy@3672 | 19 | <!--logo--> |
vhardy@3672 | 20 | |
simon@4357 | 21 | <h1>CSS Transforms</h1> |
vhardy@3672 | 22 | |
vhardy@3672 | 23 | <h2 class="no-num no-toc">[LONGSTATUS] [DATE]</h2> |
vhardy@3672 | 24 | <dl> |
vhardy@3672 | 25 | <dt>This version: |
vhardy@3672 | 26 | <dd> |
simon@4357 | 27 | <a href="[VERSION]">http://dev.w3.org/csswg/css3-transforms/</a> |
bert@5324 | 28 | <!--http://www.w3.org/TR/[YEAR]/WD-[SHORTNAME]-[CDATE]/--> |
vhardy@3672 | 29 | <dt>Latest version: |
vhardy@3672 | 30 | <dd><a |
bert@5324 | 31 | href="http://www.w3.org/TR/css3-transforms/">[LATEST]</a> |
simon@4583 | 32 | <dt>Editor's draft: |
simon@4583 | 33 | <dd><a href="http://dev.w3.org/csswg/[SHORTNAME]/">http://dev.w3.org/csswg/[SHORTNAME]/</a> |
bert@6704 | 34 | <dt>Previous version: |
bert@6704 | 35 | <dd><a href='http://www.w3.org/TR/2012/WD-css3-transforms-20120911/'>http://www.w3.org/TR/2012/WD-css3-transforms-20120911/</a> |
vhardy@3672 | 36 | <dt id="editors-list">Editors: |
vhardy@3672 | 37 | <dd>Simon Fraser (<a href="http://www.apple.com/">Apple Inc</a>) <simon.fraser @apple.com> |
vhardy@3672 | 38 | <dd>Dean Jackson (<a href="http://www.apple.com/">Apple Inc</a>) <dino @apple.com> |
vhardy@3672 | 39 | <dd>Edward O'Connor (<a href="http://www.apple.com/">Apple Inc</a>) <eoconnor @apple.com> |
dschulze@4323 | 40 | <dd>Dirk Schulze (<a href="http://www.adobe.com/">Adobe Systems, Inc</a>) <dschulze @adobe.com> |
simon@4581 | 41 | <dd>Aryeh Gregor (<a href="http://www.mozilla.org/">Mozilla</a>) <ayg @aryeh.name> |
simon@4581 | 42 | |
bert@6704 | 43 | <dt id="former-editors-list">Former Editors: |
simon@6214 | 44 | <dd>David Hyatt (<a href="http://www.apple.com/">Apple Inc</a>) <hyatt @apple.com> |
simon@6214 | 45 | <dd>Chris Marrin (<a href="http://www.apple.com/">Apple Inc</a>) <cmarrin @apple.com> |
simon@6214 | 46 | |
simon@4581 | 47 | <dt>Issues list: |
simon@4581 | 48 | <dd><a href="https://www.w3.org/Bugs/Public/buglist.cgi?query_format=advanced&product=CSS&component=Transforms&resolution=---&cmdtype=doit">in Bugzilla</a> |
simon@4581 | 49 | |
simon@4581 | 50 | <dt>Test suite: |
simon@4581 | 51 | <dd>none yet |
simon@4581 | 52 | </dl> |
vhardy@3672 | 53 | |
vhardy@3672 | 54 | <!--copyright--> |
vhardy@3672 | 55 | |
vhardy@3672 | 56 | <hr title="Separator for header"> |
vhardy@3672 | 57 | </div> |
vhardy@3672 | 58 | |
vhardy@3672 | 59 | <h2 class="no-num no-toc" id="abstract">Abstract</h2> |
ayg@5129 | 60 | |
vhardy@3672 | 61 | <p>CSS transforms allows elements styled with CSS to be transformed |
ayg@5129 | 62 | in two-dimensional or three-dimensional space. This specification is the convergence of the |
ayg@5129 | 63 | <a href="http://www.w3.org/TR/css3-2d-transforms/">CSS 2D transforms</a>, |
vhardy@3672 | 64 | <a href="http://www.w3.org/TR/css3-3d-transforms/">CSS 3D transforms</a> |
ayg@5129 | 65 | and <a href="http://www.w3.org/TR/2009/WD-SVG-Transforms-20090320/">SVG transforms</a> |
vhardy@3672 | 66 | specifications.</p> |
vhardy@3672 | 67 | |
vhardy@3672 | 68 | <h2 class="no-num no-toc" id="status">Status of this document</h2> |
vhardy@4360 | 69 | |
bert@5324 | 70 | <!--status--> |
bert@5324 | 71 | |
simon@5274 | 72 | <p> |
simon@5274 | 73 | This specification replaces the former <a href="http://www.w3.org/TR/css3-2d-transforms/" title="CSS 2D Transforms">CSS 2D Transforms</a> and <a href="http://www.w3.org/TR/css3-3d-transforms/" title="CSS 3D Transforms Module Level 3">CSS 3D Transforms</a> specifications, as well |
dschulze@5743 | 74 | as <a href="http://www.w3.org/TR/SVG-Transforms/" title="SVG Transforms 1.0">SVG Transforms</a>. |
vhardy@4360 | 75 | </p> |
vhardy@4360 | 76 | |
vhardy@3672 | 77 | <p> |
vhardy@3672 | 78 | The <a href="ChangeLog">list of changes made to this specification</a> is |
vhardy@3672 | 79 | available. |
vhardy@3672 | 80 | </p> |
vhardy@3672 | 81 | |
vhardy@3672 | 82 | <h2 class="no-num no-toc" id="contents">Table of contents</h2> |
vhardy@3672 | 83 | <!--toc--> |
vhardy@3672 | 84 | |
vhardy@3672 | 85 | <h2>Introduction</h2> |
vhardy@3672 | 86 | |
simon@4345 | 87 | <p><em>This section is not normative.</em></p> |
simon@4345 | 88 | <p> |
simon@4345 | 89 | The CSS <a href="http://www.w3.org/TR/REC-CSS2/visuren.html">visual |
dschulze@4511 | 90 | formatting model</a> describes a coordinate system within each |
simon@4345 | 91 | element is positioned. Positions and sizes in this coordinate space can |
dschulze@4511 | 92 | be thought of as being expressed in pixels, starting in the origin of point |
dschulze@4511 | 93 | with positive values proceeding to the right and down. |
simon@4345 | 94 | </p> |
simon@4345 | 95 | <p> |
dschulze@5740 | 96 | This coordinate space can be modified with the 'transform' property. Using transform, |
dschulze@5740 | 97 | elements can be translated, rotated and scaled in two or three dimensional space. |
simon@4345 | 98 | </p> |
simon@4345 | 99 | <p> |
simon@4345 | 100 | Additional properties make working with transforms easier, and allow the |
simon@4345 | 101 | author to control how nested three-dimensional transforms interact. |
simon@4345 | 102 | </p> |
simon@4345 | 103 | <ul> |
simon@4345 | 104 | <li> |
dschulze@5740 | 105 | The 'transform-origin' property provides a convenient way to control the origin about |
dschulze@5740 | 106 | which transforms on an element are applied. |
simon@4345 | 107 | </li> |
simon@4345 | 108 | <li> |
dschulze@5740 | 109 | The 'perspective' property allows the author to make child elements with |
dschulze@5740 | 110 | three-dimensional transforms appear as if they live in a common three-dimensional |
dschulze@5740 | 111 | space. |
dschulze@5740 | 112 | The 'perspective-origin' property provides control over the origin at which |
dschulze@5740 | 113 | perspective is applied, effectively changing the location of the "vanishing point". |
simon@4345 | 114 | </li> |
simon@4345 | 115 | <li> |
dschulze@5740 | 116 | The 'transform-style' property allows 3D-transformed elements and their 3D-transformed |
dschulze@5740 | 117 | descendants to share a common three-dimensional space, allowing the construction of |
dschulze@5740 | 118 | hierarchies of three-dimensional objects. |
simon@4345 | 119 | </li> |
simon@4345 | 120 | <li> |
dschulze@5740 | 121 | The 'backface-visibility' property comes into play when an element is flipped around |
dschulze@5740 | 122 | via three-dimensional transforms such that its reverse side is visible to the viewer. |
dschulze@5740 | 123 | In some situations it is desirable to hide the element in this situation, which is |
dschulze@5740 | 124 | possible using the value of ''hidden'' for this property. |
simon@4345 | 125 | </li> |
simon@4345 | 126 | </ul> |
simon@4345 | 127 | <p> |
dschulze@5740 | 128 | Note that while some values of the 'transform' property allow an element to be |
dschulze@5740 | 129 | transformed in a three-dimensional coordinate system, the elements themselves are not |
dschulze@5740 | 130 | three-dimensional objects. Instead, they exist on a two-dimensional plane (a flat |
dschulze@5740 | 131 | surface) and have no depth. |
simon@4345 | 132 | </p> |
vhardy@3672 | 133 | |
simon@4345 | 134 | <!-- ======================================================================================================= --> |
vhardy@3672 | 135 | |
simon@4345 | 136 | <h2 id="module-interactions">Module Interactions</h2> |
simon@5274 | 137 | <p>This module defines a set of CSS properties that affect the visual rendering of elements to which |
simon@5274 | 138 | those properties are applied; these effects are applied after elements have been sized and positioned according |
simon@5274 | 139 | to the <a href="http://www.w3.org/TR/CSS2/visuren.html" title="Visual formatting model">Visual formatting model</a> |
simon@5274 | 140 | from [[!CSS21]]. Some values of these properties result in the creation of a <a href="http://www.w3.org/TR/CSS2/visuren.html#containing-block" title="Visual formatting model">containing block</a>, and/or the creation of a <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index" title="Visual formatting model">stacking context</a>. |
simon@5274 | 141 | </p> |
simon@5274 | 142 | <p> |
simon@5274 | 143 | Three-dimensional transforms can also affect the visual layering of elements, and thus override the back-to-front |
simon@5274 | 144 | painting order described in <a href="http://www.w3.org/TR/CSS2/zindex.html" title="Elaborate description of Stacking Contexts">Appendix E</a> of [[!CSS21]]. |
simon@5274 | 145 | </p> |
simon@6214 | 146 | <p> |
dschulze@6300 | 147 | Transforms affect the rendering of backgounds on elements with a value of ''fixed'' for the |
dschulze@6300 | 148 | '<code class="property"><a href="http://www.w3.org/TR/css3-background/#the-background-attachment">background-attachment</a></code>' property, |
simon@6214 | 149 | which is specified in [[!CSS3BG]]. |
simon@6214 | 150 | </p> |
simon@4345 | 151 | |
simon@4365 | 152 | <h2 id="css-values">CSS Values</h2> |
simon@5274 | 153 | |
simon@5274 | 154 | <p>This specification follows the <a href="http://www.w3.org/TR/CSS21/about.html#property-defs">CSS property definition conventions</a> from [[!CSS21]]. Value types not defined in this specification are defined in CSS LevelĀ 2 RevisionĀ 1 [[!CSS21]].</p> |
simon@5274 | 155 | |
simon@5274 | 156 | <p>In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the <a href="http://www.w3.org/TR/CSS21/cascade.html#value-def-inherit">inherit</a> keyword as their property value. For readability it has not been repeated explicitly. |
simon@4345 | 157 | |
simon@4345 | 158 | |
simon@4345 | 159 | <h2 id="definitions">Definitions</h2> |
simon@4345 | 160 | <p> When used in this specification, terms have the meanings assigned in |
simon@4345 | 161 | this section. |
simon@4345 | 162 | </p> |
simon@4345 | 163 | <dl> |
dschulze@4483 | 164 | <dt id="TermBoundingBox"><dfn>bounding box</dfn></dt> |
dschulze@4483 | 165 | <dd> |
dschulze@4483 | 166 | <p> |
ayg@4742 | 167 | A bounding box is the object bounding box for all SVG elements |
ayg@4742 | 168 | without an associated CSS layout box and the border box for all |
ayg@4742 | 169 | other elements. The bounding box of a table is the border box |
ayg@4742 | 170 | of its |
ayg@4742 | 171 | <a href="http://www.w3.org/TR/CSS21/tables.html#model">table wrapper box</a>, |
ayg@4742 | 172 | not its table box. |
dschulze@4483 | 173 | </p> |
dschulze@4483 | 174 | </dd> |
dschulze@4483 | 175 | |
simon@4365 | 176 | <dt id="TermTransformableElement"><dfn>transformable element</dfn></dt> |
simon@4345 | 177 | <dd> |
vhardy@3672 | 178 | <p> |
eoconnor@6894 | 179 | A transformable element is an element in one of these |
eoconnor@6894 | 180 | categories: |
vhardy@3672 | 181 | </p> |
eoconnor@6894 | 182 | <ul> |
eoconnor@6894 | 183 | <li> |
eoconnor@6894 | 184 | an element whose |
eoconnor@6894 | 185 | layout is governed by the CSS box model which is either |
eoconnor@6894 | 186 | a |
eoconnor@6894 | 187 | <a href="http://www.w3.org/TR/CSS2/visuren.html#block-level">block-level</a> |
eoconnor@6894 | 188 | or |
eoconnor@6894 | 189 | <a href="http://www.w3.org/TR/CSS2/visuren.html#x13">atomic inline-level element</a>, |
eoconnor@6894 | 190 | or whose 'display' property |
eoconnor@6894 | 191 | computes to ''table-row'', |
eoconnor@6894 | 192 | ''table-row-group'', |
eoconnor@6894 | 193 | ''table-header-group'', |
eoconnor@6894 | 194 | ''table-footer-group'', |
eoconnor@6894 | 195 | ''table-cell'', or |
eoconnor@6894 | 196 | ''table-caption'' [[!CSS21]] |
eoconnor@6894 | 197 | </li> |
eoconnor@6894 | 198 | <li> |
dschulze@6901 | 199 | an element in the SVG namespace and not governed by the CSS box model which has |
dschulze@6901 | 200 | the attributes 'transform', 'patternTransform' or 'gradientTransform' [[!SVG11]] |
eoconnor@6894 | 201 | </li> |
eoconnor@6894 | 202 | </ul> |
simon@4345 | 203 | </dd> |
simon@4345 | 204 | |
dschulze@5931 | 205 | <dt id="TermLocalCoordinateSystem"><dfn>local coordinate system</dfn></dt> |
dschulze@5931 | 206 | <dd> |
dschulze@5931 | 207 | <p> |
dschulze@5931 | 208 | In general, a coordinate system defines locations and distances on the current |
dschulze@5931 | 209 | canvas. The current local coordinate system (also user coordinate system) is the |
dschulze@5931 | 210 | coordinate system that is currently active and which is used to define how |
dschulze@5931 | 211 | coordinates and lengths are located and computed, respectively, on the current |
dschulze@5931 | 212 | canvas. |
dschulze@5931 | 213 | </p> |
dschulze@5931 | 214 | </dd> |
dschulze@6300 | 215 | <dt id="TermUserCoordinateSystem"><dfn>user coordinate system</dfn></dt> |
dschulze@6300 | 216 | <dd> |
dschulze@6300 | 217 | <p> |
dschulze@6300 | 218 | See definition of <span class="term">local coordinate system</span>. |
dschulze@6300 | 219 | </p> |
dschulze@6300 | 220 | </dd> |
ayg@4750 | 221 | <dt id="TermPerspectiveMatrix"><dfn>perspective matrix</dfn></dt> |
simon@4345 | 222 | <dd> |
simon@4345 | 223 | <p> |
dschulze@5931 | 224 | A matrix computed from the values of the 'perspective' and 'perspective-origin' properties as described <a href="#perspective-matrix-computation">below</a>. |
simon@4365 | 225 | </p> |
simon@4365 | 226 | </dd> |
simon@4365 | 227 | |
simon@4366 | 228 | <dt id="TermTransformationMatrix"><dfn>transformation matrix</dfn></dt> |
simon@4365 | 229 | <dd> |
simon@4365 | 230 | <p> |
dschulze@5931 | 231 | A matrix that defines the mathematical mapping from one coordinate system into another. It is computed from the values of the 'transform' and 'transform-origin' properties as described <a href="#transformation-matrix-computation">below</a>. |
dschulze@5931 | 232 | </p> |
dschulze@5931 | 233 | </dd> |
dschulze@5931 | 234 | |
dschulze@5931 | 235 | <dt id="TermCurrentTransformationMatrix"><dfn>current transformation matrix (CTM)</dfn></dt> |
dschulze@5931 | 236 | <dd> |
dschulze@5931 | 237 | <p> |
dschulze@5931 | 238 | A matrix that defines the mapping from the local coordinate system into the viewport coordinate system. |
simon@4365 | 239 | </p> |
simon@4365 | 240 | </dd> |
simon@4365 | 241 | |
ayg@5127 | 242 | <dt id="TermAccumulated3DTransformationMatrix"> |
ayg@5127 | 243 | <dfn>accumulated 3D transformation matrix</dfn> |
ayg@5127 | 244 | </dt> |
ayg@5127 | 245 | <dd> |
ayg@5127 | 246 | <p> |
ayg@5127 | 247 | A matrix computed for elements in a |
ayg@5127 | 248 | <a href="#d-rendering-context">3D rendering context</a>, as |
ayg@5127 | 249 | described |
ayg@5127 | 250 | <a href="#accumulated-3d-transformation-matrix-computation">below</a>. |
ayg@5127 | 251 | </p> |
ayg@5127 | 252 | </dd> |
dschulze@5690 | 253 | |
dschulze@5690 | 254 | <dt id="TermIdentityTransformFunction"> |
dschulze@5931 | 255 | <dfn>identity transform function</dfn> |
dschulze@5690 | 256 | </dt> |
dschulze@5690 | 257 | <dd> |
dschulze@5690 | 258 | <p> |
dschulze@5690 | 259 | A <a href="#transform-functions">transform function</a> that is equivalent to a |
dschulze@5690 | 260 | identity 4x4 matrix (see <a href="#mathematical-description">Mathematical |
dschulze@5690 | 261 | Description of Transform Functions</a>). Examples for identity transform functions |
dschulze@5740 | 262 | are ''translate(0)'', ''translate3d(0, 0, 0)'', ''translateX(0)'', |
dschulze@5740 | 263 | ''translateY(0)'', ''translateZ(0)'', ''scale(1)'', ''scaleX(1)'', ''scaleY(1)'', |
dschulze@5740 | 264 | ''scaleZ(1)'', ''rotate(0)'', ''rotate3d(1, 1, 1, 0)'', ''rotateX(0)'', |
dschulze@6608 | 265 | ''rotateY(0)'', ''rotateZ(0)'', ''skew(0, 0)'', ''skewX(0)'', ''skewY(0)'', ''matrix(1, 0, 0, 1, 0, |
dschulze@5740 | 266 | 0)'' and ''matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)''. A special |
dschulze@5900 | 267 | case is perspective: ''perspective(infinity)''. The value of m<sub>34</sub> |
dschulze@5740 | 268 | becomes infinitesimal small and the transform function is therefore assumed to be |
dschulze@5740 | 269 | equal to the identity matrix. |
dschulze@5690 | 270 | </p> |
dschulze@5690 | 271 | </dd> |
ayg@5127 | 272 | |
simon@4365 | 273 | <dt id="Term3DRenderingContext"><dfn>3D rendering context</dfn></dt> |
simon@4365 | 274 | <dd> |
simon@4365 | 275 | <p> |
dschulze@6300 | 276 | A containing block hierarchy of one or more levels, instantiated by elements with a |
dschulze@6300 | 277 | computed value for the 'transform-style' property of ''preserve-3d'', whose elements |
dschulze@6300 | 278 | share a common three-dimensional coordinate system. |
dschulze@4321 | 279 | </p> |
simon@4345 | 280 | </dd> |
simon@4345 | 281 | </dl> |
simon@4345 | 282 | |
simon@4345 | 283 | <!-- ======================================================================================================= --> |
ayg@5129 | 284 | |
dschulze@5740 | 285 | <h2 id="two-dimensional-subset"> |
dschulze@5726 | 286 | Two Dimensional Subset |
dschulze@5726 | 287 | </h2> |
dschulze@5726 | 288 | |
dschulze@5726 | 289 | <p> |
dschulze@5726 | 290 | UAs may not always be able to render three-dimensional transforms and then just |
dschulze@5726 | 291 | support a two-dimensional subset of this specification. In this case <a |
dschulze@5726 | 292 | href="#three-d-transform-functions">three-dimensional transforms</a> and the |
dschulze@5726 | 293 | properties 'transform-style', 'perspective', 'perspective-origin' and |
dschulze@5726 | 294 | 'backface-visibility' must not be supported. Section <a |
dschulze@5726 | 295 | href="#transform-3d-rendering">3D Transform Rendering</a> does not apply. |
dschulze@5726 | 296 | Matrix decomposing uses the technique taken from the "unmatrix" method in "Graphics |
dschulze@5726 | 297 | Gems II, edited by Jim Arvo", simplified for the 2D case. Section |
dschulze@5900 | 298 | <a href="#mathematical-description">Mathematical Description of Transform Functions</a> |
dschulze@5900 | 299 | is still effective but can be reduced by using a 3x3 transformation matrix where |
dschulze@5900 | 300 | <em>a</em> equals <em>m<sub>11</sub></em>, <em>b</em> equals <em>m<sub>12</sub></em>, |
dschulze@5900 | 301 | <em>c</em> equals <em>m<sub>21</sub></em>, <em>d</em> equals <em>m<sub>22</sub></em>, |
dschulze@5900 | 302 | <em>e</em> equals <em>m<sub>41</sub></em> and <em>f</em> equals <em>m<sub>42</sub></em> |
dschulze@5900 | 303 | (see <a href="#MatrixDefined">A 2D 3x2 matrix with six parameter</a>). |
dschulze@5726 | 304 | </p> |
dschulze@5726 | 305 | |
dschulze@5726 | 306 | <div class="figure"> |
dschulze@5726 | 307 | <img src="3x3matrix.png" alt="3x3 matrix" title="\begin{bmatrix} a & c & e \\ b |
dschulze@5726 | 308 | & d & f \\ 0 & 0 & 1 \end{bmatrix}" width="82" height="79"> |
dschulze@5726 | 309 | <p class="caption"> |
dschulze@5726 | 310 | 3x3 matrix for two-dimensional transformations. |
dschulze@5726 | 311 | </p> |
dschulze@5726 | 312 | </div> |
dschulze@5726 | 313 | |
dschulze@5726 | 314 | <div class="example"> |
dschulze@5726 | 315 | <p> |
dschulze@5726 | 316 | Authors can easily provide a fallback if UAs do not provide support for |
dschulze@5726 | 317 | three-dimensional transforms. The following example has two property definitions |
dschulze@5726 | 318 | for 'transform'. The first one consists of two two-dimensional transform |
dschulze@5726 | 319 | functions. The second one has a two-dimensional and a three-dimensional transform |
dschulze@5726 | 320 | function. |
dschulze@5726 | 321 | </p> |
dschulze@5726 | 322 | <pre>div { |
dschulze@5726 | 323 | transform: scale(2) rotate(45deg); |
dschulze@5726 | 324 | transform: scale(2) rotate3d(0, 0, 1, 45deg); |
dschulze@5726 | 325 | }</pre> |
dschulze@5726 | 326 | |
dschulze@5726 | 327 | <p> |
dschulze@5726 | 328 | With 3D support, the second definition will override the first one. Without 3D |
dschulze@5726 | 329 | support, the second definition is invalid and a UA falls back to the first |
dschulze@5726 | 330 | definition. |
dschulze@5726 | 331 | </p> |
dschulze@5726 | 332 | </div> |
dschulze@5726 | 333 | |
dschulze@5726 | 334 | <!-- ======================================================================================================= --> |
dschulze@5726 | 335 | |
simon@4345 | 336 | <h2 id="transform-rendering">The Transform Rendering Model</h2> |
dschulze@6908 | 337 | <p><em>This section is normative.</em></p> |
dschulze@5740 | 338 | <p> |
dschulze@5931 | 339 | Specifying a value other than ''none'' for the ''transform'' property establishes a |
dschulze@5931 | 340 | new <span class="term">local coordinate system</span> at the element that it is |
dschulze@5931 | 341 | applied to. The mapping from where the element would have rendered into that local |
dschulze@5931 | 342 | coordinate system is given by the element's <span class="term">transformation |
dschulze@5931 | 343 | matrix</span>. Transformations are cumulative. That is, elements establish their local |
dschulze@5931 | 344 | coordinate system within the coordinate system of their parent. From the perspective |
dschulze@5931 | 345 | of the user, an element effectively accumulates all the 'transform' properties of its |
dschulze@5931 | 346 | ancestors as well as any local transform applied to it. The accumulation of these |
dschulze@5931 | 347 | transforms defines a <span class="term">current transformation matrix (CTM)</span> for |
dschulze@5931 | 348 | the element. |
dschulze@5740 | 349 | </p> |
dschulze@5740 | 350 | <p> |
dschulze@5932 | 351 | The coordinate space is a |
dschulze@5740 | 352 | coordinate system with two axes: the X axis increases horizontally to the right; the |
eoconnor@6037 | 353 | Y axis increases vertically downwards. Three-dimensional transform functions extend |
dschulze@5740 | 354 | this coordinate space into three dimensions, adding a Z axis perpendicular to the |
dschulze@5740 | 355 | plane of the screen, that increases towards the viewer. |
dschulze@5740 | 356 | </p> |
dschulze@5937 | 357 | |
dschulze@5936 | 358 | <div class="figure"> |
dschulze@5937 | 359 | <img src="coordinates.svg" width="270" height="240" alt="Demonstration of the initial coordinate space"> |
dschulze@5936 | 360 | <p class="caption"> |
dschulze@5936 | 361 | Demonstration of the initial coordinate space. |
dschulze@5936 | 362 | </p> |
dschulze@5936 | 363 | </div> |
dschulze@5937 | 364 | |
dschulze@5740 | 365 | <p id="transformation-matrix-computation"> |
dschulze@5931 | 366 | The <span class="term">transformation matrix</span> is computed |
dschulze@5740 | 367 | from the 'transform' and 'transform-origin' properties as follows: |
dschulze@5740 | 368 | <ol> |
dschulze@5740 | 369 | <li>Start with the identity matrix.</li> |
dschulze@5740 | 370 | <li>Translate by the computed X, Y and Z values of 'transform-origin'</li> |
dschulze@6960 | 371 | <li>Multiply by each of the transform functions in 'transform' property from left to right</li> |
dschulze@5740 | 372 | <li>Translate by the negated computed X, Y and Z values of 'transform-origin'</li> |
dschulze@5740 | 373 | </ol> |
bert@6704 | 374 | |
dschulze@5740 | 375 | <p> |
dschulze@5740 | 376 | Transforms apply to <span class="term">transformable elements</span>. |
dschulze@5740 | 377 | </p> |
simon@4358 | 378 | |
dschulze@5740 | 379 | <div class="example"> |
dschulze@5740 | 380 | <pre> |
simon@4358 | 381 | div { |
simon@4358 | 382 | transform: translate(100px, 100px); |
simon@4358 | 383 | } |
simon@4358 | 384 | </pre> |
simon@4358 | 385 | <p>This transform moves the element by 100 pixels in both the X and Y directions.</p> |
simon@4358 | 386 | <div class="figure"> |
dschulze@5740 | 387 | <img src="examples/translate1.svg" alt="The 100px translation in X and Y" |
dschulze@5740 | 388 | width="470" height="250"> |
simon@4358 | 389 | </div> |
simon@4358 | 390 | </div> |
dschulze@5625 | 391 | |
dschulze@5625 | 392 | <div class="example"> |
dschulze@5740 | 393 | <pre>div { |
dschulze@5625 | 394 | height: 100px; width: 100px; |
dschulze@5625 | 395 | transform-origin: 50px 50px; |
dschulze@5625 | 396 | transform: rotate(45deg); |
dschulze@5740 | 397 | }</pre> |
dschulze@5625 | 398 | |
dschulze@5740 | 399 | <p> |
dschulze@5740 | 400 | The 'transform-origin' property moves the point of origin by 50 pixels in both |
dschulze@5740 | 401 | the X and Y directions. The transform rotates the element clockwise by 45Ā° about |
dschulze@5740 | 402 | the point of origin. After all transform functions were applied, the translation |
dschulze@5740 | 403 | of the origin gets translated back by -50 pixels in both the X and Y directions. |
dschulze@5740 | 404 | </p> |
dschulze@5625 | 405 | |
dschulze@5740 | 406 | <div class="figure"> |
dschulze@5740 | 407 | <img alt="The point of origin gets translated temporary" src="examples/origin1.svg" |
dschulze@5740 | 408 | width="735" height="250"> |
dschulze@5740 | 409 | </div> |
dschulze@5625 | 410 | </div> |
ayg@5129 | 411 | |
simon@4358 | 412 | <div class="example"> |
dschulze@5740 | 413 | <pre> |
simon@4358 | 414 | div { |
simon@4358 | 415 | height: 100px; width: 100px; |
simon@4358 | 416 | transform: translate(80px, 80px) scale(1.5, 1.5) rotate(45deg); |
simon@4358 | 417 | } |
simon@4358 | 418 | </pre> |
dschulze@5740 | 419 | <p> |
dschulze@5740 | 420 | This transform moves the element by 80 pixels in both the X and Y directions, then |
dschulze@5740 | 421 | scales the element by 150%, then rotates it 45° clockwise about the Z axis. |
dschulze@5740 | 422 | Note that the scale and rotation operate about the center of the element, since |
dschulze@5740 | 423 | the element has the default transform-origin of ''50% 50%''. |
dschulze@5740 | 424 | </p> |
simon@4358 | 425 | |
dschulze@5740 | 426 | <div class="figure"> |
dschulze@5740 | 427 | <img src="examples/compound_transform.svg" alt="The transform specified above" |
dschulze@5740 | 428 | width="270" height="270"> |
dschulze@5740 | 429 | </div> |
dschulze@5740 | 430 | |
dschulze@5740 | 431 | <p> |
dschulze@5740 | 432 | Note that an identical rendering can be obtained by nesting elements with the |
dschulze@5740 | 433 | equivalent transforms: |
dschulze@5740 | 434 | </p> |
dschulze@5740 | 435 | |
dschulze@5740 | 436 | <pre> |
simon@4358 | 437 | <div style="transform: translate(80px, 80px)"> |
dschulze@4602 | 438 | <div style="transform: scale(1.5, 1.5)"> |
dschulze@4602 | 439 | <div style="transform: rotate(45deg)"></div> |
dschulze@4602 | 440 | </div> |
dschulze@5740 | 441 | </div></pre> |
dschulze@5740 | 442 | </div> |
simon@4358 | 443 | |
simon@4345 | 444 | <p> |
dschulze@6901 | 445 | For elements whose layout is governed by the CSS box model, the transform property |
dschulze@6901 | 446 | does not affect the flow of the |
dschulze@5740 | 447 | content surrounding the transformed element. However, the extent of the overflow |
dschulze@5740 | 448 | area takes into account transformed elements. This behavior is similar to what |
dschulze@5740 | 449 | happens when elements are offset via relative positioning. Therefore, if the value |
dschulze@5740 | 450 | of the 'overflow' property is ''scroll'' or ''auto'', scrollbars will appear as |
dschulze@5740 | 451 | needed to see content that is transformed outside the visible area. |
vhardy@3672 | 452 | </p> |
vhardy@3672 | 453 | <p> |
dschulze@6901 | 454 | For elements whose layout is governed by the CSS box model, any value other than |
dschulze@6901 | 455 | ''none'' for the transform results in |
dschulze@5740 | 456 | the creation of both a stacking context and a containing block. The object acts as a |
vhardy@3672 | 457 | containing block for fixed positioned descendants. |
vhardy@3672 | 458 | </p> |
simon@4345 | 459 | <p class="issue"> |
ayg@5090 | 460 | Is this effect on position:fixed necessary? If so, need to go into more detail here |
dschulze@5740 | 461 | about why fixed positioned objects should do this, i.e., that it's much harder to |
dschulze@6900 | 462 | implement otherwise. See <a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=16328">Bug 16328</a>. |
dschulze@4321 | 463 | </p> |
ayg@5088 | 464 | <p> |
simon@6214 | 465 | <a href="http://www.w3.org/TR/css3-background/#fixed0">Fixed backgrounds</a> |
simon@6214 | 466 | on the root element are affected by any transform specified for that element. |
simon@6214 | 467 | For all other elements that are effected by a transform (i.e. have a transform |
dschulze@6300 | 468 | applied to them, or to any of their ancestor elements), a value of ''fixed'' for the |
dschulze@6904 | 469 | 'background-attachment' property is treated as if it had a value of ''scroll''. The |
dschulze@6904 | 470 | computed value of 'background-attachment' is not affected. |
bert@6704 | 471 | |
ayg@5088 | 472 | <p class="note"> |
ayg@5106 | 473 | If the root element is transformed, the transformation applies |
ayg@5106 | 474 | to the entire canvas, including any background specified for |
simon@6214 | 475 | the root element. Since <a href="http://www.w3.org/TR/css3-background/#special-backgrounds"> |
simon@6214 | 476 | the background painting area for the root element</a> is the entire |
ayg@5106 | 477 | canvas, which is infinite, the transformation might cause parts |
ayg@5106 | 478 | of the background that were originally off-screen to appear. |
ayg@5106 | 479 | For example, if the root element's background were repeating |
dschulze@6300 | 480 | dots, and a transformation of ''scale(0.5)'' were specified on the |
ayg@5106 | 481 | root element, the dots would shrink to half their size, but |
ayg@5106 | 482 | there will be twice as many, so they still cover the whole |
ayg@5106 | 483 | viewport. |
ayg@5106 | 484 | </p> |
ayg@5129 | 485 | |
simon@4345 | 486 | <h3 id="transform-3d-rendering">3D Transform Rendering</h3> |
simon@4345 | 487 | |
simon@4345 | 488 | <p> |
simon@4345 | 489 | Normally, elements render as flat planes, and are rendered into the same plane |
simon@4345 | 490 | as their containing block. Often this is the plane shared by the rest of the page. |
simon@4345 | 491 | Two-dimensional transform functions can alter the appearance of an element, but |
simon@4363 | 492 | that element is still rendered into the same plane as its containing block. |
simon@4345 | 493 | </p> |
simon@4345 | 494 | <p> |
simon@4345 | 495 | Three-dimensional transforms can result in transformation matrices with a non-zero |
simon@6215 | 496 | Z component (where the Z axis projects out of the plane of the screen). This can result |
simon@6215 | 497 | in an element rendering on a different plane than that of its containing block. This |
simon@6215 | 498 | may affect the front-to-back rendering order of that element relative to other elements, |
simon@6215 | 499 | as well as causing it to intersect with other elements. This behavior depends on whether the |
simon@6215 | 500 | element is a member of a <span class="term">3D rendering context</span>, as described below. |
simon@4345 | 501 | </p> |
simon@4366 | 502 | <div class="issue"> |
simon@4345 | 503 | <p class="desc">This description does not exactly match what WebKit implements. Perhaps |
dschulze@6900 | 504 | it should be changed to match current implementations? See |
dschulze@6900 | 505 | <a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=19637">Bug 19637</a>.</p> |
vhardy@3672 | 506 | </div> |
simon@4366 | 507 | |
simon@4345 | 508 | <div class="example"> |
simon@4356 | 509 | <p>This example shows the effect of three-dimensional transform applied to an element. |
simon@4356 | 510 | </p> |
simon@4345 | 511 | <pre> |
simon@4364 | 512 | <style> |
dschulze@4602 | 513 | div { |
dschulze@4602 | 514 | height: 150px; |
dschulze@4602 | 515 | width: 150px; |
dschulze@4602 | 516 | } |
dschulze@4602 | 517 | .container { |
dschulze@4602 | 518 | border: 1px solid black; |
dschulze@4602 | 519 | } |
dschulze@4602 | 520 | .transformed { |
dschulze@4602 | 521 | transform: rotateY(50deg); |
dschulze@4602 | 522 | } |
simon@4364 | 523 | </style> |
simon@4366 | 524 | |
simon@4356 | 525 | <div class="container"> |
simon@4356 | 526 | <div class="transformed"></div> |
simon@4356 | 527 | </div> |
ayg@5129 | 528 | </pre> |
simon@4366 | 529 | <div class="figure"> |
simon@4366 | 530 | <img src="examples/simple-3d-example.png" width="210" height="190" alt="Div with a rotateY transform."> |
simon@4366 | 531 | </div> |
simon@4358 | 532 | <p>The transform is a 50° rotation about the vertical, Y axis. Note how this makes the blue box appear |
simon@4356 | 533 | narrower, but not three-dimensional. |
simon@4345 | 534 | </p> |
simon@4345 | 535 | </div> |
simon@4366 | 536 | |
simon@4366 | 537 | <p> |
dschulze@6300 | 538 | The 'perspective' and 'perspective-origin' properties can be used to add a feeling |
dschulze@6300 | 539 | of depth to a scene by making elements higher on the Z axis (closer to the viewer) |
dschulze@6300 | 540 | appear larger, and those further away to appear smaller. The scaling is proportional |
dschulze@6300 | 541 | to <var>d</var>/(<var>d</var> − <var>Z</var>) where <var>d</var>, the value of |
dschulze@6300 | 542 | 'perspective', is the distance from the drawing plane to the the assumed position of |
dschulze@6300 | 543 | the viewer's eye. |
simon@4366 | 544 | </p> |
ayg@5128 | 545 | |
ayg@5128 | 546 | <div class="figure"> |
ayg@5128 | 547 | <img alt="Diagram of scale vs. Z position" src="perspective_distance.png"> |
ayg@5128 | 548 | <p class="caption"> |
dschulze@6300 | 549 | Diagrams showing how scaling depends on the 'perspective' property and Z position. |
dschulze@6300 | 550 | In the top diagram, <var>Z</var> is half of <var>d</var>. In order to make it |
dschulze@6300 | 551 | appear that the original circle (solid outline) appears at <var>Z</var> (dashed |
dschulze@6300 | 552 | circle), the circle is scaled up by a factor of two, resulting in the light blue |
dschulze@6300 | 553 | circle. In the bottom diagram, the circle is scaled down by a factor of one-third |
dschulze@6300 | 554 | to make it appear behind the original position. |
ayg@5128 | 555 | </p> |
ayg@5128 | 556 | </div> |
ayg@5128 | 557 | |
ayg@5128 | 558 | <p> |
ayg@5128 | 559 | Normally the assumed position of the viewer's eye is |
ayg@5128 | 560 | centered on a drawing. This position can be moved if |
ayg@5128 | 561 | desired – for example, if a web page contains |
ayg@5128 | 562 | multiple drawings that should share a common perspective |
dschulze@6300 | 563 | – by setting 'perspective-origin'. |
ayg@5128 | 564 | </p> |
ayg@5128 | 565 | |
ayg@5128 | 566 | <div class="figure"> |
ayg@5128 | 567 | <img alt="Diagram of different perspective-origin" src="perspective_origin.png"> |
ayg@5128 | 568 | <p class="caption"> |
ayg@5128 | 569 | Diagram showing the effect of moving the perspective origin upward. |
ayg@5128 | 570 | </p> |
ayg@5128 | 571 | </div> |
ayg@5128 | 572 | |
simon@4453 | 573 | <p id="perspective-matrix-computation"> |
simon@4366 | 574 | The <a href="#TermPerspectiveMatrix"><i>perspective matrix</i></a> is computed as follows: |
simon@4366 | 575 | <ol> |
simon@4366 | 576 | <li>Start with the identity matrix.</li> |
dschulze@6300 | 577 | <li>Translate by the computed X and Y values of 'perspective-origin'</li> |
dschulze@6300 | 578 | <li>Multiply by the matrix that would be obtained from the '<a href="#perspective-function"><code class="css">perspective(<length>)</code></a>' transform function, where the length is provided by the value of the 'perspective' property</li> |
dschulze@6300 | 579 | <li>Translate by the negated computed X and Y values of 'perspective-origin'</li> |
simon@4366 | 580 | </ol> |
bert@6704 | 581 | |
ayg@5129 | 582 | |
simon@4356 | 583 | <div class="example"> |
simon@4356 | 584 | <p>This example shows how perspective can be used to cause three-dimensional transforms to appear more realistic. |
simon@4356 | 585 | </p> |
simon@4356 | 586 | <pre> |
simon@4364 | 587 | <style> |
dschulze@4602 | 588 | div { |
dschulze@4602 | 589 | height: 150px; |
dschulze@4602 | 590 | width: 150px; |
dschulze@4602 | 591 | } |
dschulze@4602 | 592 | .container { |
dschulze@4602 | 593 | perspective: 500px; |
dschulze@4602 | 594 | border: 1px solid black; |
dschulze@4602 | 595 | } |
dschulze@4602 | 596 | .transformed { |
dschulze@4602 | 597 | transform: rotateY(50deg); |
dschulze@4602 | 598 | } |
simon@4364 | 599 | </style> |
simon@4366 | 600 | |
simon@4356 | 601 | <div class="container"> |
simon@4356 | 602 | <div class="transformed"></div> |
simon@4356 | 603 | </div> |
ayg@5129 | 604 | </pre> |
simon@4366 | 605 | <div class="figure"> |
simon@4366 | 606 | <img src="examples/simple-perspective-example.png" width="210" height="190" alt="Div with a rotateY transform, |
simon@4366 | 607 | and perspective on its container"> |
simon@4366 | 608 | </div> |
simon@4366 | 609 | <p>The inner element has the same transform as in the previous example, but its rendering is now influenced by the perspective |
simon@4356 | 610 | property on its parent element. Perspective causes vertices that have positive Z coordinates (closer to the viewer) |
dschulze@4602 | 611 | to be scaled up in X and Y, and those further away (negative Z coordinates) to be scaled down, giving an appearance of depth. |
simon@4356 | 612 | </p> |
simon@4356 | 613 | </div> |
simon@4356 | 614 | |
simon@4345 | 615 | <p> |
simon@4366 | 616 | An element with a three-dimensional transform that is not contained in a |
simon@4366 | 617 | <span class="term">3D rendering context</span> renders with the appropriate |
simon@4366 | 618 | transform applied, but does not intersect with any other elements. The three-dimensional |
simon@4366 | 619 | transform in this case can be considered just as a painting effect, like two-dimensional |
ayg@5129 | 620 | transforms. Similarly, the transform does not affect painting order. For example, a transform with a |
simon@4366 | 621 | positive Z translation may make an element look larger, but does not cause that element |
simon@4366 | 622 | to render in front of elements with no translation in Z. |
simon@4366 | 623 | </p> |
simon@4366 | 624 | <p> |
simon@4345 | 625 | An element with a three-dimensional transform that is contained in a |
simon@4345 | 626 | <span class="term">3D rendering context</span> can visibly interact with other elements |
ayg@5129 | 627 | in that same 3D rendering context; the set of elements participating in the same |
simon@4345 | 628 | <span class="term">3D rendering context</span> may obscure each other or intersect, |
simon@4345 | 629 | based on their computed transforms. They are rendered as if they are all siblings, |
simon@4345 | 630 | positioned in a common 3D coordinate space. The position of each element in that three-dimensional |
simon@4345 | 631 | space is determined by accumulating the transformation matrices |
simon@4345 | 632 | up from the element that establishes the <span class="term">3D rendering context</span> |
simon@4345 | 633 | through each element that is a containing block for the given element, as described below. |
simon@4345 | 634 | </p> |
simon@4345 | 635 | |
simon@4366 | 636 | <div class="example"> |
simon@4366 | 637 | <pre> |
simon@4366 | 638 | <style> |
dschulze@4602 | 639 | div { |
dschulze@4602 | 640 | height: 150px; |
dschulze@4602 | 641 | width: 150px; |
dschulze@4602 | 642 | } |
dschulze@4602 | 643 | .container { |
dschulze@4602 | 644 | perspective: 500px; |
dschulze@4602 | 645 | border: 1px solid black; |
dschulze@4602 | 646 | } |
dschulze@4602 | 647 | .transformed { |
dschulze@4602 | 648 | transform: rotateY(50deg); |
dschulze@4602 | 649 | background-color: blue; |
dschulze@4602 | 650 | } |
dschulze@4602 | 651 | .child { |
dschulze@4602 | 652 | transform-origin: top left; |
dschulze@4602 | 653 | transform: rotateX(40deg); |
dschulze@4602 | 654 | background-color: lime; |
dschulze@4602 | 655 | } |
simon@4366 | 656 | </style> |
simon@4366 | 657 | |
simon@4366 | 658 | <div class="container"> |
dschulze@4602 | 659 | <div class="transformed"> |
dschulze@4602 | 660 | <div class="child"></div> |
dschulze@4602 | 661 | </div> |
simon@4366 | 662 | </div> |
simon@4366 | 663 | </pre> |
dschulze@6300 | 664 | <p>This example shows how nested 3D transforms are rendered in the absence of ''transform-style: preserve-3d''. The blue div is transformed as in the previous example, with its rendering influenced by the perspective on its parent element. The lime element also has a 3D transform, which is a rotation about the X axis (anchored at the top, by virtue of the transform-origin). However, the lime element is being rendered into the plane of its parent because it is not a member of a 3D rendering context; the parent is "flattening". |
simon@4366 | 665 | </p> |
simon@4366 | 666 | <div class="figure"> |
simon@4366 | 667 | <img src="examples/3d-rendering-context-flat.png" width="240" height="200" alt="Nested 3D transforms, with flattening"> |
simon@4366 | 668 | </div> |
simon@4366 | 669 | </div> |
simon@4366 | 670 | |
simon@4365 | 671 | <p>Elements establish and participate in 3D rendering contexts as follows:</p> |
simon@4365 | 672 | <ul> |
simon@4365 | 673 | <li> |
simon@4365 | 674 | A <span class="term">3D rendering context</span> is established by a |
dschulze@6300 | 675 | a <span class="term">transformable element</span> whose computed value for 'transform-style' is |
dschulze@6300 | 676 | ''preserve-3d'', and which itself is not part of a 3D rendering context. |
simon@4365 | 677 | Note that such an element is always a containing block. An element that establishes a 3D rendering context |
simon@4365 | 678 | also participates in that context. |
simon@4365 | 679 | </li> |
simon@4365 | 680 | <li> |
dschulze@6300 | 681 | An element whose computed value for 'transform-style' is |
dschulze@6300 | 682 | ''preserve-3d'', and which itself participates in a |
simon@4365 | 683 | <span class="term">3D rendering context</span>, extends that 3D rendering context rather than establishing |
simon@4365 | 684 | a new one. |
simon@4365 | 685 | </li> |
simon@4365 | 686 | <li> |
simon@4365 | 687 | An element participates in a <span class="term">3D rendering context</span> if its containing block |
simon@4365 | 688 | establishes or extends a <span class="term">3D rendering context</span>. |
simon@4365 | 689 | </li> |
simon@4365 | 690 | </ul> |
ayg@5127 | 691 | <p id="accumulated-3d-transformation-matrix-computation"> |
simon@4345 | 692 | The final value of the transform used to render an element in a <span class="term">3D rendering context</span> |
ayg@5127 | 693 | is computed by accumulating an |
ayg@5127 | 694 | <a href="#TermAccumulated3DTransformationMatrix"> |
ayg@5127 | 695 | accumulated 3D transformation matrix</a> as follows: |
simon@4345 | 696 | </p> |
simon@4345 | 697 | <ol> |
ayg@5127 | 698 | <li>Start with the identity matrix.</li> |
ayg@5129 | 699 | <li>For each containing block between the root of the <span class="term">3D rendering context</span> |
simon@4366 | 700 | and the element in question: |
simon@4365 | 701 | <ol> |
dschulze@6300 | 702 | <li>multiply the accumulated matrix with the <span class="term">perspective matrix</span> |
ayg@4751 | 703 | on the element's containing block (if any). That containing block is not necessarily a member |
simon@4366 | 704 | of the 3D rendering context.</li> |
simon@4366 | 705 | <li>apply to the accumulated matrix a translation equivalent to the horizontal and vertical offset of the element relative to |
dschulze@6893 | 706 | its containing block as specified by the CSS visual formatting model.</li> |
dschulze@6300 | 707 | <li>multiply the accumulated matrix with the <span class="term">transformation matrix</span>.</li> |
simon@4365 | 708 | </ol> |
simon@4345 | 709 | </li> |
simon@4345 | 710 | </ol> |
ayg@5129 | 711 | |
simon@4345 | 712 | <div class="example"> |
simon@4345 | 713 | <pre> |
simon@4364 | 714 | <style> |
dschulze@4602 | 715 | div { |
dschulze@4602 | 716 | height: 150px; |
dschulze@4602 | 717 | width: 150px; |
dschulze@4602 | 718 | } |
dschulze@4602 | 719 | .container { |
dschulze@4602 | 720 | perspective: 500px; |
dschulze@4602 | 721 | border: 1px solid black; |
dschulze@4602 | 722 | } |
dschulze@4602 | 723 | .transformed { |
dschulze@4602 | 724 | <b>transform-style: preserve-3d</b>; |
dschulze@4602 | 725 | transform: rotateY(50deg); |
dschulze@4602 | 726 | background-color: blue; |
dschulze@4602 | 727 | } |
dschulze@4602 | 728 | .child { |
dschulze@4602 | 729 | transform-origin: top left; |
dschulze@4602 | 730 | transform: rotateX(40deg); |
dschulze@4602 | 731 | background-color: lime; |
dschulze@4602 | 732 | } |
simon@4364 | 733 | </style> |
simon@4345 | 734 | </pre> |
dschulze@6300 | 735 | <p> |
dschulze@6300 | 736 | This example is identical to the previous example, with the addition of |
dschulze@6300 | 737 | ''transform-style: preserve-3d'' on the blue element. The blue element now |
dschulze@6300 | 738 | establishes a 3D rendering context, of which the lime element is a member. Now |
dschulze@6300 | 739 | both blue and lime elements share a common three-dimensional space, so the lime |
dschulze@6300 | 740 | element renders as tilting out from its parent, influenced by the perspective on |
dschulze@6300 | 741 | the container. |
simon@4345 | 742 | </p> |
simon@4345 | 743 | <div class="figure"> |
simon@4364 | 744 | <img src="examples/3d-rendering-context-3d.png" width="240" height="200" alt="Nested 3D transforms, with preserve-3d."> |
simon@4345 | 745 | </div> |
simon@4345 | 746 | </div> |
simon@4345 | 747 | |
simon@4366 | 748 | <p> |
simon@4701 | 749 | Elements in the same <span class="term">3D rendering context</span> may intersect with each other. User agents must |
simon@4702 | 750 | render intersection by subdividing the planes of intersecting elements as described by |
simon@4701 | 751 | <a href="http://en.wikipedia.org/wiki/Newell's_algorithm">Newell's algorithm</a>. |
simon@4366 | 752 | </p> |
simon@4366 | 753 | <p> |
simon@4366 | 754 | Untransformed elements in a <span class="term">3D rendering context</span> render on the Z=0 plane, yet may still |
simon@4366 | 755 | intersect with transformed elements. |
simon@4366 | 756 | </p> |
simon@4366 | 757 | <p> |
simon@4366 | 758 | Within a <span class="term">3D rendering context</span>, the rendering order of non-intersecting elements is |
simon@4366 | 759 | based on their position on the Z axis after the application of the accumulated transform. Elements at the same |
simon@4366 | 760 | Z position render in <a href="http://www.w3.org/TR/CSS2/zindex.html#painting-order">stacking context order</a>. |
simon@4366 | 761 | </p> |
simon@4366 | 762 | |
simon@4364 | 763 | <div class="example"> |
simon@4345 | 764 | <pre> |
simon@4364 | 765 | <style> |
simon@4364 | 766 | .container { |
simon@4364 | 767 | background-color: rgba(0, 0, 0, 0.3); |
simon@4364 | 768 | transform-style: preserve-3d; |
simon@4364 | 769 | perspective: 500px; |
simon@4364 | 770 | } |
simon@4364 | 771 | .container > div { |
simon@4364 | 772 | position: absolute; |
simon@4364 | 773 | left: 0; |
simon@4364 | 774 | } |
simon@4364 | 775 | .container > :first-child { |
simon@4364 | 776 | transform: rotateY(45deg); |
simon@4364 | 777 | background-color: orange; |
simon@4364 | 778 | top: 10px; |
simon@4364 | 779 | height: 135px; |
simon@4364 | 780 | } |
simon@4364 | 781 | .container > :last-child { |
simon@4364 | 782 | transform: translateZ(40px); |
simon@4364 | 783 | background-color: rgba(0, 0, 255, 0.75); |
simon@4364 | 784 | top: 50px; |
simon@4364 | 785 | height: 100px; |
simon@4364 | 786 | } |
simon@4364 | 787 | </style> |
simon@4364 | 788 | |
simon@4364 | 789 | <div class="container"> |
simon@4364 | 790 | <div></div> |
simon@4364 | 791 | <div></div> |
simon@4364 | 792 | </div> |
simon@4345 | 793 | </pre> |
simon@4364 | 794 | <p> |
simon@4364 | 795 | This example shows show elements in a 3D rendering context can intersect. The container element establishes |
simon@4364 | 796 | a 3D rendering context for itself and its two children. The children intersect with eachother, and |
simon@4364 | 797 | the orange element also intersects with the container. |
simon@4364 | 798 | </p> |
simon@4364 | 799 | <div class="figure"> |
simon@4364 | 800 | <img src="examples/3d-intersection.png" width="200" height="200" alt="Intersecting sibling elements."> |
simon@4364 | 801 | </div> |
simon@4345 | 802 | </div> |
ayg@5129 | 803 | |
simon@4367 | 804 | <p> |
simon@4367 | 805 | Using three-dimensional transforms, it's possible to transform an element such that its reverse side |
ayg@5901 | 806 | is towards the viewer. 3D-transformed elements show the same content on both sides, so the reverse side |
simon@4701 | 807 | looks like a mirror-image of the front side (as if the element were projected onto a sheet of glass). |
simon@4367 | 808 | Normally, elements whose reverse side is towards the viewer remain visible. However, the |
dschulze@6300 | 809 | 'backface-visibility' property allows the author to make an element invisible |
simon@4367 | 810 | when its reverse side is towards the viewer. This behavior is "live"; if an element with |
dschulze@6300 | 811 | ''backface-visibility: hidden'' were animating, |
simon@4367 | 812 | such that its front and reverse sides were alternately visible, then it would only be visible when the |
simon@4367 | 813 | front side were towards the viewer. |
simon@4367 | 814 | </p> |
simon@4364 | 815 | |
ayg@5901 | 816 | <h3 id="processing-of-perspective-transformed-boxes"> |
ayg@5901 | 817 | Processing of Perspective-Transformed Boxes |
ayg@5901 | 818 | </h3> |
ayg@5901 | 819 | |
ayg@5901 | 820 | <div class="issue"> |
ayg@5901 | 821 | <p class="desc"> |
ayg@5901 | 822 | This is a first pass at an attempt to precisely specify how |
ayg@5901 | 823 | exactly to transform elements using the provided matrices. |
ayg@5901 | 824 | It might not be ideal, and implementer feedback is |
ayg@5901 | 825 | encouraged. See <a |
ayg@5901 | 826 | href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=15605">bug |
ayg@5901 | 827 | 15605</a>. |
ayg@5901 | 828 | </p> |
ayg@5901 | 829 | </div> |
ayg@5901 | 830 | |
ayg@5901 | 831 | <p> |
dschulze@6300 | 832 | The <span class="term">accumulated |
dschulze@6300 | 833 | 3D transformation matrix</span> is a 4×4 matrix, while the |
dschulze@6300 | 834 | objects to be transformed are two-dimensional boxes. To |
dschulze@6300 | 835 | transform each corner (<var>a</var>, <var>b</var>) of a box, the |
dschulze@6300 | 836 | matrix must first be applied to (<var>a</var>, <var>b</var>, 0, |
dschulze@6300 | 837 | 1), which will result in a four-dimensional point (<var>x</var>, |
dschulze@6300 | 838 | <var>y</var>, <var>z</var>, <var>w</var>). This is transformed |
dschulze@6300 | 839 | back to a three-dimensional point (<var>x</var>′, <var>y</var>′, |
dschulze@6300 | 840 | <var>z</var>′) as follows: |
ayg@5901 | 841 | </p> |
ayg@5901 | 842 | |
ayg@5901 | 843 | <p> |
dschulze@6300 | 844 | If <var>w</var> > 0, (<var>x</var>′, |
dschulze@6300 | 845 | <var>y</var>′, <var>z</var>′) = |
dschulze@6300 | 846 | (<var>x</var>/<var>w</var>, <var>y</var>/<var>w</var>, |
dschulze@6300 | 847 | <var>z</var>/<var>w</var>). |
ayg@5901 | 848 | </p> |
ayg@5901 | 849 | |
ayg@5901 | 850 | <p> |
dschulze@6300 | 851 | If <var>w</var> = 0, (<var>x</var>′, <var>y</var>′, |
dschulze@6300 | 852 | <var>z</var>′) = (<var>x</var> ⋅ <var>n</var>, |
dschulze@6300 | 853 | <var>y</var> ⋅ <var>n</var>, <var>z</var> ⋅ |
dschulze@6300 | 854 | <var>n</var>). <var>n</var> is an implementation-dependent value |
dschulze@6300 | 855 | that should be chosen so that <var>x</var>′ or |
dschulze@6300 | 856 | <var>y</var>′ is much larger than the viewport size, if |
dschulze@6300 | 857 | possible. For example, (5px, 22px, 0px, 0) might become (5000px, |
dschulze@6300 | 858 | 22000px, 0px), with <var>n</var> = 1000, but this value of |
dschulze@6300 | 859 | <var>n</var> would be too small for (0.1px, 0.05px, 0px, 0). |
dschulze@6300 | 860 | This specification does not define the value of <var>n</var> |
dschulze@6300 | 861 | exactly. Conceptually, (<var>x</var>′, |
dschulze@6300 | 862 | <var>y</var>′, <var>z</var>′) is <a |
dschulze@6300 | 863 | href="http://en.wikipedia.org/wiki/Plane_at_infinity">infinitely |
dschulze@6300 | 864 | far</a> in the direction (<var>x</var>, <var>y</var>, |
dschulze@6300 | 865 | <var>z</var>). |
ayg@5901 | 866 | </p> |
ayg@5901 | 867 | |
ayg@5901 | 868 | <p> |
dschulze@6300 | 869 | If <var>w</var> < 0 for all four corners of the transformed |
dschulze@6300 | 870 | box, the box is not rendered. |
ayg@5901 | 871 | </p> |
ayg@5901 | 872 | |
ayg@5901 | 873 | <p> |
dschulze@6300 | 874 | If <var>w</var> < 0 for one to three corners of the |
dschulze@6300 | 875 | transformed box, the box must be replaced by a polygon that has |
dschulze@6300 | 876 | any parts with <var>w</var> < 0 cut out. This will in general |
dschulze@6300 | 877 | be a polygon with three to five vertices, of which exactly two |
dschulze@6300 | 878 | will have <var>w</var> = 0 and the rest <var>w</var> > 0. |
dschulze@6300 | 879 | These vertices are then transformed to three-dimensional points |
dschulze@6300 | 880 | using the rules just stated. Conceptually, a point with |
dschulze@6300 | 881 | <var>w</var> < 0 is "behind" the viewer, so should not be |
dschulze@6300 | 882 | visible. |
ayg@5901 | 883 | </p> |
ayg@5901 | 884 | |
ayg@5901 | 885 | <div class="example"> |
ayg@5901 | 886 | <pre><style> |
ayg@5901 | 887 | .transformed { |
ayg@5901 | 888 | height: 100px; |
ayg@5901 | 889 | width: 100px; |
ayg@5901 | 890 | background: lime; |
ayg@5901 | 891 | transform: perspective(50px) translateZ(100px); |
ayg@5901 | 892 | } |
ayg@5901 | 893 | </style></pre> |
ayg@5901 | 894 | |
ayg@5901 | 895 | <p> |
dschulze@6300 | 896 | All of the box's corners have <var>z</var>-coordinates greater |
dschulze@6300 | 897 | than the perspective. This means that the box is behind the |
dschulze@6300 | 898 | viewer and will not display. Mathematically, the point |
dschulze@6300 | 899 | (<var>x</var>, <var>y</var>) first becomes (<var>x</var>, |
dschulze@6300 | 900 | <var>y</var>, 0, 1), then is translated to (<var>x</var>, |
dschulze@6300 | 901 | <var>y</var>, 100, 1), and then applying the perspective |
dschulze@6300 | 902 | results in (<var>x</var>, <var>y</var>, 100, −1). The |
dschulze@6300 | 903 | <var>w</var>-coordinate is negative, so it does not display. |
dschulze@6300 | 904 | An implementation that doesn't handle the <var>w</var> < 0 |
dschulze@6300 | 905 | case separately might incorrectly display this point as |
dschulze@6300 | 906 | (−<var>x</var>, −<var>y</var>, −100), |
dschulze@6300 | 907 | dividing by −1 and mirroring the box. |
ayg@5901 | 908 | </p> |
ayg@5901 | 909 | </div> |
ayg@5901 | 910 | |
ayg@5901 | 911 | <div class="example"> |
ayg@5901 | 912 | <pre><style> |
ayg@5901 | 913 | .transformed { |
ayg@5901 | 914 | height: 100px; |
ayg@5901 | 915 | width: 100px; |
ayg@5901 | 916 | background: radial-gradient(yellow, blue); |
ayg@5901 | 917 | transform: perspective(50px) translateZ(50px); |
ayg@5901 | 918 | } |
ayg@5901 | 919 | </style></pre> |
ayg@5901 | 920 | |
ayg@5901 | 921 | <p> |
dschulze@6300 | 922 | Here, the box is translated upward so that it sits at the same |
dschulze@6300 | 923 | place the viewer is looking from. This is like bringing the |
dschulze@6300 | 924 | box closer and closer to one's eye until it fills the entire |
dschulze@6300 | 925 | field of vision. Since the default transform-origin is at the |
dschulze@6300 | 926 | center of the box, which is yellow, the screen will be filled |
dschulze@6300 | 927 | with yellow. |
ayg@5901 | 928 | </p> |
ayg@5901 | 929 | |
ayg@5901 | 930 | <p> |
dschulze@6300 | 931 | Mathematically, the point (<var>x</var>, <var>y</var>) first |
dschulze@6300 | 932 | becomes (<var>x</var>, <var>y</var>, 0, 1), then is translated |
dschulze@6300 | 933 | to (<var>x</var>, <var>y</var>, 50, 1), then becomes |
dschulze@6300 | 934 | (<var>x</var>, <var>y</var>, 50, 0) after applying perspective. |
dschulze@6300 | 935 | Relative to the transform-origin at the center, the upper-left |
dschulze@6300 | 936 | corner was (−50, −50), so it becomes (−50, |
dschulze@6300 | 937 | −50, 50, 0). This is transformed to something |
dschulze@6300 | 938 | very far to the upper left, such as (−5000, −5000, |
dschulze@6300 | 939 | 5000). Likewise the other corners are sent very far away. The |
dschulze@6300 | 940 | radial gradient is stretched over the whole box, now enormous, |
dschulze@6300 | 941 | so the part that's visible without scrolling should be the |
dschulze@6300 | 942 | color of the middle pixel: yellow. However, since the box is |
dschulze@6300 | 943 | not actually infinite, the user can still scroll to the edges |
dschulze@6300 | 944 | to see the blue parts. |
ayg@5901 | 945 | </p> |
ayg@5901 | 946 | </div> |
ayg@5901 | 947 | |
ayg@5901 | 948 | <div class="example"> |
ayg@5901 | 949 | <pre><style> |
ayg@5901 | 950 | .transformed { |
ayg@5901 | 951 | height: 50px; |
ayg@5901 | 952 | width: 50px; |
ayg@5901 | 953 | background: lime; |
ayg@5901 | 954 | border: 25px solid blue; |
ayg@5901 | 955 | transform-origin: left; |
ayg@5901 | 956 | transform: perspective(50px) rotateY(-45deg); |
ayg@5901 | 957 | } |
ayg@5901 | 958 | </style></pre> |
ayg@5901 | 959 | |
ayg@5901 | 960 | <p> |
dschulze@6300 | 961 | The box will be rotated toward the viewer, with the left edge |
dschulze@6300 | 962 | staying fixed while the right edge swings closer. The right |
dschulze@6300 | 963 | edge will be at about <var>z</var> = 70.7px, which is closer |
dschulze@6300 | 964 | than the perspective of 50px. Therefore, the rightmost edge |
dschulze@6300 | 965 | will vanish ("behind" the viewer), and the visible part will |
dschulze@6300 | 966 | stretch out infinitely far to the right. |
ayg@5901 | 967 | </p> |
ayg@5901 | 968 | |
ayg@5901 | 969 | <p> |
dschulze@6300 | 970 | Mathematically, the top right vertex of the box was originally |
dschulze@6300 | 971 | (100, −50), relative to the transform-origin. It is first |
dschulze@6300 | 972 | expanded to (100, −50, 0, 1). After applying the |
dschulze@6300 | 973 | transform specified, this will get mapped to about (70.71, |
dschulze@6300 | 974 | −50, 70.71, −0.4142). This has <var>w</var> = |
dschulze@6300 | 975 | −0.4142 < 0, so we need to slice away the part of the |
dschulze@6300 | 976 | box with <var>w</var> < 0. This results in the new |
dschulze@6300 | 977 | top-right vertex being (50, −50, 50, 0). This is then |
dschulze@6300 | 978 | mapped to some faraway point in the same direction, such as |
dschulze@6300 | 979 | (5000, −5000, 5000), which is up and to the right from |
dschulze@6300 | 980 | the transform-origin. Something similar is done to the lower |
dschulze@6300 | 981 | right corner, which gets mapped far down and to the right. The |
dschulze@6300 | 982 | resulting box stretches far past the edge of the screen. |
ayg@5901 | 983 | </p> |
ayg@5901 | 984 | |
ayg@5901 | 985 | <p> |
dschulze@6300 | 986 | Again, the rendered box is still finite, so the user can scroll |
dschulze@6300 | 987 | to see the whole thing if he or she chooses. However, the |
dschulze@6300 | 988 | right part has been chopped off. No matter how far the user |
dschulze@6300 | 989 | scrolls, the rightmost 30px or so of the original box will not |
dschulze@6300 | 990 | be visible. The blue border was only 25px wide, so it will be |
dschulze@6300 | 991 | visible on the left, top, and bottom, but not the right. |
ayg@5901 | 992 | </p> |
ayg@5901 | 993 | |
ayg@5901 | 994 | <p> |
dschulze@6300 | 995 | The same basic procedure would apply if one or three vertices |
dschulze@6300 | 996 | had <var>w</var> < 0. However, in that case the result of |
dschulze@6300 | 997 | truncating the <var>w</var> < 0 part would be a triangle or |
dschulze@6300 | 998 | pentagon instead of a quadrilateral. |
ayg@5901 | 999 | </p> |
ayg@5901 | 1000 | </div> |
ayg@5901 | 1001 | |
simon@4345 | 1002 | <!-- ======================================================================================================= --> |
dschulze@4321 | 1003 | |
vhardy@3672 | 1004 | <h2 id="transform-property"> |
dschulze@6300 | 1005 | The 'transform' Property |
vhardy@3672 | 1006 | </h2> |
vhardy@3672 | 1007 | <p> |
simon@4345 | 1008 | A transformation is applied to the coordinate system an element |
dschulze@6300 | 1009 | renders in through the 'transform' property. This property contains a |
vhardy@3672 | 1010 | list of <a href="#transform-functions">transform functions</a>. The |
vhardy@3672 | 1011 | final transformation value for a coordinate system is obtained by converting |
dschulze@6300 | 1012 | each function in the list to its corresponding matrix like defined in <a |
dschulze@6300 | 1013 | href="#mathematical-description">Mathematical Description of Transform |
dschulze@6300 | 1014 | Functions</a>, then multiplying the matrices. |
vhardy@3672 | 1015 | </p> |
vhardy@3672 | 1016 | <table class="propdef"> |
vhardy@3672 | 1017 | <tbody> |
vhardy@3672 | 1018 | <tr> |
vhardy@3672 | 1019 | <td> |
vhardy@3672 | 1020 | <em>Name:</em> |
vhardy@3672 | 1021 | </td> |
vhardy@3672 | 1022 | <td> |
vhardy@3672 | 1023 | <dfn id="effects">transform</dfn> |
vhardy@3672 | 1024 | </td> |
vhardy@3672 | 1025 | </tr> |
vhardy@3672 | 1026 | <tr> |
vhardy@3672 | 1027 | <td> |
vhardy@3672 | 1028 | <em>Value:</em> |
vhardy@3672 | 1029 | </td> |
vhardy@3672 | 1030 | <td> |
vhardy@3672 | 1031 | none | <transform-function> [ <transform-function> ]* |
vhardy@3672 | 1032 | </td> |
vhardy@3672 | 1033 | </tr> |
vhardy@3672 | 1034 | <tr> |
vhardy@3672 | 1035 | <td> |
vhardy@3672 | 1036 | <em>Initial:</em> |
vhardy@3672 | 1037 | </td> |
vhardy@3672 | 1038 | <td> |
vhardy@3672 | 1039 | none |
vhardy@3672 | 1040 | </td> |
vhardy@3672 | 1041 | </tr> |
vhardy@3672 | 1042 | <tr> |
vhardy@3672 | 1043 | <td> |
dbaron@7338 | 1044 | <em>Applies to:</em> |
vhardy@3672 | 1045 | </td> |
vhardy@3672 | 1046 | <td> |
dschulze@4321 | 1047 | <a href="#TermTransformableElement">transformable elements</a> |
vhardy@3672 | 1048 | </td> |
vhardy@3672 | 1049 | </tr> |
vhardy@3672 | 1050 | <tr> |
vhardy@3672 | 1051 | <td> |
vhardy@3672 | 1052 | <em>Inherited:</em> |
vhardy@3672 | 1053 | </td> |
vhardy@3672 | 1054 | <td> |
vhardy@3672 | 1055 | no |
vhardy@3672 | 1056 | </td> |
vhardy@3672 | 1057 | </tr> |
vhardy@3672 | 1058 | <tr> |
vhardy@3672 | 1059 | <td> |
vhardy@3672 | 1060 | <em>Percentages:</em> |
vhardy@3672 | 1061 | </td> |
vhardy@3672 | 1062 | <td> |
dschulze@7030 | 1063 | refer to the size of |
dschulze@7030 | 1064 | <var>bounding box</var> |
vhardy@3672 | 1065 | </td> |
vhardy@3672 | 1066 | </tr> |
vhardy@3672 | 1067 | <tr> |
vhardy@3672 | 1068 | <td> |
vhardy@3672 | 1069 | <em>Media:</em> |
vhardy@3672 | 1070 | </td> |
vhardy@3672 | 1071 | <td> |
vhardy@3672 | 1072 | visual |
vhardy@3672 | 1073 | </td> |
vhardy@3672 | 1074 | </tr> |
vhardy@3672 | 1075 | <tr> |
vhardy@3672 | 1076 | <td> |
vhardy@3672 | 1077 | <em>Computed value:</em> |
vhardy@3672 | 1078 | </td> |
vhardy@3672 | 1079 | <td> |
ayg@4747 | 1080 | As specified, but with relative lengths converted into |
ayg@4747 | 1081 | absolute lengths. |
vhardy@3672 | 1082 | </td> |
vhardy@3672 | 1083 | </tr> |
dbaron@7349 | 1084 | <tr> |
dbaron@7349 | 1085 | <td> |
dbaron@7349 | 1086 | <em>Animatable:</em> |
dbaron@7349 | 1087 | </td> |
dbaron@7349 | 1088 | <td> |
dbaron@7349 | 1089 | as <a href="#animation">transform</a> |
dbaron@7349 | 1090 | </td> |
dbaron@7349 | 1091 | </tr> |
vhardy@3672 | 1092 | </tbody> |
simon@4334 | 1093 | </table> |
ayg@5129 | 1094 | |
dschulze@6300 | 1095 | <p> |
dschulze@6300 | 1096 | Any value other than ''none'' for the transform results in the creation of both a |
dschulze@6300 | 1097 | stacking context and a containing block. The object acts as a containing block for |
dschulze@6300 | 1098 | fixed positioned descendants. |
dschulze@6300 | 1099 | </p> |
simon@4363 | 1100 | |
simon@4334 | 1101 | <!-- ======================================================================================================= --> |
ayg@5129 | 1102 | |
vhardy@3672 | 1103 | <h2 id="transform-origin-property"> |
dschulze@6300 | 1104 | The 'transform-origin' Property |
vhardy@3672 | 1105 | </h2> |
vhardy@3672 | 1106 | <table class="propdef"> |
vhardy@3672 | 1107 | <tbody> |
vhardy@3672 | 1108 | <tr> |
vhardy@3672 | 1109 | <td> |
vhardy@3672 | 1110 | <em>Name:</em> |
vhardy@3672 | 1111 | </td> |
vhardy@3672 | 1112 | <td> |
vhardy@3672 | 1113 | <dfn id="transform-origin">transform-origin</dfn> |
vhardy@3672 | 1114 | </td> |
vhardy@3672 | 1115 | </tr> |
vhardy@3672 | 1116 | <tr> |
vhardy@3672 | 1117 | <td> |
vhardy@3672 | 1118 | <em>Value:</em> |
vhardy@3672 | 1119 | </td> |
vhardy@3672 | 1120 | <td> |
dschulze@7808 | 1121 | [ left | center | right | top | bottom | <var><percentage></var> | <var><length></var> ]<br> |
dschulze@7808 | 1122 | |<br> |
dschulze@7808 | 1123 | [ left | center | right | <var><percentage></var> | <var><length></var> ]<br> |
dschulze@7808 | 1124 | [ top | center | bottom | <var><percentage></var> | <var><length></var> ] <var><length></var>? |
vhardy@3672 | 1125 | </td> |
vhardy@3672 | 1126 | </tr> |
vhardy@3672 | 1127 | <tr> |
vhardy@3672 | 1128 | <td> |
vhardy@3672 | 1129 | <em>Initial:</em> |
vhardy@3672 | 1130 | </td> |
vhardy@3672 | 1131 | <td> |
dschulze@5740 | 1132 | 50% 50% |
vhardy@3672 | 1133 | </td> |
vhardy@3672 | 1134 | </tr> |
vhardy@3672 | 1135 | <tr> |
vhardy@3672 | 1136 | <td> |
dbaron@7338 | 1137 | <em>Applies to:</em> |
vhardy@3672 | 1138 | </td> |
vhardy@3672 | 1139 | <td> |
simon@4366 | 1140 | <a href="#TermTransformableElement">transformable elements</a> |
vhardy@3672 | 1141 | </td> |
vhardy@3672 | 1142 | </tr> |
vhardy@3672 | 1143 | <tr> |
vhardy@3672 | 1144 | <td> |
vhardy@3672 | 1145 | <em>Inherited:</em> |
vhardy@3672 | 1146 | </td> |
vhardy@3672 | 1147 | <td> |
vhardy@3672 | 1148 | no |
vhardy@3672 | 1149 | </td> |
vhardy@3672 | 1150 | </tr> |
vhardy@3672 | 1151 | <tr> |
vhardy@3672 | 1152 | <td> |
vhardy@3672 | 1153 | <em>Percentages:</em> |
vhardy@3672 | 1154 | </td> |
vhardy@3672 | 1155 | <td> |
dschulze@7030 | 1156 | refer to the size of |
dschulze@7030 | 1157 | <var>bounding box</var> |
vhardy@3672 | 1158 | </td> |
vhardy@3672 | 1159 | </tr> |
vhardy@3672 | 1160 | <tr> |
vhardy@3672 | 1161 | <td> |
vhardy@3672 | 1162 | <em>Media:</em> |
vhardy@3672 | 1163 | </td> |
vhardy@3672 | 1164 | <td> |
vhardy@3672 | 1165 | visual |
vhardy@3672 | 1166 | </td> |
vhardy@3672 | 1167 | </tr> |
vhardy@3672 | 1168 | <tr> |
vhardy@3672 | 1169 | <td> |
vhardy@3672 | 1170 | <em>Computed value:</em> |
vhardy@3672 | 1171 | </td> |
vhardy@3672 | 1172 | <td> |
vhardy@3672 | 1173 | For <length> the absolute value, otherwise a percentage |
vhardy@3672 | 1174 | </td> |
vhardy@3672 | 1175 | </tr> |
dbaron@7349 | 1176 | <tr> |
dbaron@7349 | 1177 | <td> |
dbaron@7349 | 1178 | <em>Animatable:</em> |
dbaron@7349 | 1179 | </td> |
dbaron@7349 | 1180 | <td> |
dbaron@7349 | 1181 | as <a href="http://dev.w3.org/csswg/css3-transitions/#animtype-simple-list">simple list</a> of <a href="http://dev.w3.org/csswg/css3-transitions/#animtype-lpcalc">length, percentage, or calc</a> |
dbaron@7349 | 1182 | </td> |
dbaron@7349 | 1183 | </tr> |
vhardy@3672 | 1184 | </tbody> |
vhardy@3672 | 1185 | </table> |
dschulze@5740 | 1186 | |
dschulze@5740 | 1187 | <p> |
dschulze@5740 | 1188 | The default value for SVG elements without associated CSS layout box is ''0 0''. |
dschulze@5740 | 1189 | </p> |
simon@4366 | 1190 | |
simon@4366 | 1191 | <p> |
dschulze@5658 | 1192 | The values of the 'transform' and 'transform-origin' properties are used to compute |
dschulze@6300 | 1193 | the <span class="term">transformation matrix</span>, as |
dschulze@5658 | 1194 | described above. |
simon@4366 | 1195 | </p> |
simon@4366 | 1196 | |
dschulze@5658 | 1197 | <p> |
dschulze@5740 | 1198 | If only one value is specified, the second value is assumed to be ''center''. If one |
dschulze@5740 | 1199 | or two values are specified, the third value is assumed to be ''0px''. |
dschulze@4483 | 1200 | </p> |
ayg@5129 | 1201 | |
dschulze@5658 | 1202 | <p> |
dschulze@5658 | 1203 | If two or more values are defined and either no value is a keyword, or the only |
dschulze@5740 | 1204 | used keyword is ''center'', then the first value represents the horizontal position |
dschulze@5740 | 1205 | (or offset) and the second represents the vertical position (or offset). A third |
dschulze@5740 | 1206 | value always represents the Z position (or offset) and must be of type |
dschulze@5740 | 1207 | <var><length></var>. |
ayg@4703 | 1208 | </p> |
ayg@4703 | 1209 | |
dschulze@7030 | 1210 | <dl> |
dschulze@7030 | 1211 | <dt><var><percentage></var> |
dschulze@7030 | 1212 | |
dschulze@7030 | 1213 | <dd><p>A percentage for the horizontal offset is relative to the width of the |
dschulze@7030 | 1214 | <var>bounding box</var>. |
dschulze@7030 | 1215 | A percentage for the vertical offset is relative to height of the |
dschulze@7030 | 1216 | <var>bounding box</var>. The value for the horizontal |
dschulze@7030 | 1217 | and vertical offset represent an offset from the top left corner of the |
dschulze@7030 | 1218 | <var>bounding box</var>. |
dschulze@7030 | 1219 | </p> |
dschulze@7030 | 1220 | |
dschulze@7030 | 1221 | <dt><var><length></var> |
dschulze@7030 | 1222 | |
dschulze@7030 | 1223 | <dd> |
dschulze@7030 | 1224 | <p>A length value gives a fixed length as the offset. The value for the horizontal |
dschulze@7030 | 1225 | and vertical offset represent an offset from the top left corner of the |
dschulze@7030 | 1226 | <var>bounding box</var>. |
dschulze@7030 | 1227 | </p> |
dschulze@7030 | 1228 | |
dschulze@7030 | 1229 | <p>For SVG elements without an associated CSS layout box the horizontal |
dschulze@7030 | 1230 | and vertical offset represent an offset from the point of origin of |
dschulze@7030 | 1231 | the element's local coordinate space. |
dschulze@7030 | 1232 | </p> |
dschulze@7030 | 1233 | </dd> |
dschulze@7030 | 1234 | |
dschulze@7030 | 1235 | <dt><dfn title="''top''!!'transform-origin' value">''top''</dfn> |
dschulze@7030 | 1236 | |
dschulze@7030 | 1237 | <dd>Computes to ''0%'' for the vertical position. |
dschulze@7030 | 1238 | |
dschulze@7030 | 1239 | <dt><dfn title="''right''!!'transform-origin' value">''right''</dfn> |
dschulze@7030 | 1240 | |
dschulze@7030 | 1241 | <dd>Computes to ''100%'' for the horizontal position. |
dschulze@7030 | 1242 | |
dschulze@7030 | 1243 | <dt><dfn title="''bottom''!!'transform-origin' value">''bottom''</dfn> |
dschulze@7030 | 1244 | |
dschulze@7030 | 1245 | <dd>Computes to ''100%'' for the vertical position. |
dschulze@7030 | 1246 | |
dschulze@7030 | 1247 | <dt><dfn title="''left''!!'transform-origin' value">''left''</dfn> |
dschulze@7030 | 1248 | |
dschulze@7030 | 1249 | <dd>Computes to ''0%'' for the horizontal position. |
dschulze@7030 | 1250 | |
dschulze@7030 | 1251 | <dt><dfn title="''center''!!'transform-origin' value">''center''</dfn> |
dschulze@7030 | 1252 | |
dschulze@7030 | 1253 | <dd>Computes to ''50%'' (''left 50%'') for the |
dschulze@7030 | 1254 | horizontal position if the horizontal position is not otherwise |
dschulze@7030 | 1255 | specified, or ''50%'' (''top 50%'') for the vertical position if |
dschulze@7030 | 1256 | it is. |
dschulze@7030 | 1257 | </dl> |
simon@4366 | 1258 | |
ayg@4737 | 1259 | <p> |
bert@5324 | 1260 | The <a href="http://www.w3.org/TR/cssom/#resolved-value">resolved value</a> |
dschulze@6300 | 1261 | of 'transform-origin' is the |
ayg@4737 | 1262 | <a href="http://www.w3.org/TR/CSS21/cascade.html#used-value">used value</a> |
ayg@4737 | 1263 | (i.e., percentages are resolved to absolute lengths). |
ayg@4737 | 1264 | </p> |
ayg@4737 | 1265 | |
simon@4345 | 1266 | <!-- ======================================================================================================= --> |
simon@4345 | 1267 | |
simon@4345 | 1268 | <h2 id="transform-style-property"> |
dschulze@6300 | 1269 | The 'transform-style' Property |
simon@4345 | 1270 | </h2> |
simon@4345 | 1271 | <table class="propdef"> |
simon@4345 | 1272 | <tbody> |
simon@4345 | 1273 | <tr> |
simon@4345 | 1274 | <td> |
simon@4345 | 1275 | <em>Name:</em> |
simon@4345 | 1276 | </td> |
simon@4345 | 1277 | <td> |
simon@4345 | 1278 | <dfn id="transform-style">transform-style</dfn> |
simon@4345 | 1279 | </td> |
simon@4345 | 1280 | </tr> |
simon@4345 | 1281 | <tr> |
simon@4345 | 1282 | <td> |
simon@4345 | 1283 | <em>Value:</em> |
simon@4345 | 1284 | </td> |
simon@4345 | 1285 | <td> |
simon@4345 | 1286 | flat | preserve-3d |
simon@4345 | 1287 | </td> |
simon@4345 | 1288 | </tr> |
simon@4345 | 1289 | <tr> |
simon@4345 | 1290 | <td> |
simon@4345 | 1291 | <em>Initial:</em> |
simon@4345 | 1292 | </td> |
simon@4345 | 1293 | <td> |
simon@4345 | 1294 | flat |
simon@4345 | 1295 | </td> |
simon@4345 | 1296 | </tr> |
simon@4345 | 1297 | <tr> |
simon@4345 | 1298 | <td> |
dbaron@7338 | 1299 | <em>Applies to:</em> |
simon@4345 | 1300 | </td> |
simon@4345 | 1301 | <td> |
simon@4345 | 1302 | <a href="#TermTransformableElement">transformable elements</a> |
simon@4345 | 1303 | </td> |
simon@4345 | 1304 | </tr> |
simon@4345 | 1305 | <tr> |
simon@4345 | 1306 | <td> |
simon@4345 | 1307 | <em>Inherited:</em> |
simon@4345 | 1308 | </td> |
simon@4345 | 1309 | <td> |
simon@4345 | 1310 | no |
simon@4345 | 1311 | </td> |
simon@4345 | 1312 | </tr> |
simon@4345 | 1313 | <tr> |
simon@4345 | 1314 | <td> |
simon@4345 | 1315 | <em>Percentages:</em> |
simon@4345 | 1316 | </td> |
simon@4345 | 1317 | <td> |
simon@4345 | 1318 | N/A |
simon@4345 | 1319 | </td> |
simon@4345 | 1320 | </tr> |
simon@4345 | 1321 | <tr> |
simon@4345 | 1322 | <td> |
simon@4345 | 1323 | <em>Media:</em> |
simon@4345 | 1324 | </td> |
simon@4345 | 1325 | <td> |
simon@4345 | 1326 | visual |
simon@4345 | 1327 | </td> |
simon@4345 | 1328 | </tr> |
simon@4345 | 1329 | <tr> |
simon@4345 | 1330 | <td> |
simon@4345 | 1331 | <em>Computed value:</em> |
simon@4345 | 1332 | </td> |
simon@4345 | 1333 | <td> |
simon@4345 | 1334 | Same as specified value. |
simon@4345 | 1335 | </td> |
simon@4345 | 1336 | </tr> |
dbaron@7349 | 1337 | <tr> |
dbaron@7349 | 1338 | <td> |
dbaron@7349 | 1339 | <em>Animatable:</em> |
dbaron@7349 | 1340 | </td> |
dbaron@7349 | 1341 | <td> |
dbaron@7349 | 1342 | no |
dbaron@7349 | 1343 | </td> |
dbaron@7349 | 1344 | </tr> |
simon@4345 | 1345 | </tbody> |
simon@4345 | 1346 | </table> |
simon@4345 | 1347 | |
simon@4345 | 1348 | <p> |
dschulze@6300 | 1349 | A value of ''preserve-3d'' for 'transform-style' establishes a stacking context. |
simon@4345 | 1350 | </p> |
simon@4345 | 1351 | |
simon@4345 | 1352 | <p> |
simon@4345 | 1353 | The following CSS property values require the user agent to create a flattened representation of |
ayg@5129 | 1354 | the descendant elements before they can be applied, and therefore override the behavior of |
dschulze@6904 | 1355 | ''transform-style: preserve-3d'':</p> |
dschulze@6904 | 1356 | <ul> |
dschulze@6904 | 1357 | <li>'overflow': any value other than ''visible''.</li> |
dschulze@6904 | 1358 | <li>'opacity': any value other than ''1''.</li> |
dschulze@6904 | 1359 | <li>'filter': any value other than ''none''.</li> |
dschulze@6904 | 1360 | </ul> |
bert@6704 | 1361 | |
dschulze@6904 | 1362 | <p> |
dschulze@6904 | 1363 | The computed value of 'transform-style' is not affected. |
dschulze@6904 | 1364 | </p> |
dschulze@6904 | 1365 | |
simon@4366 | 1366 | <p> |
dschulze@6300 | 1367 | The values of the 'transform' and 'transform-origin' properties are used to compute |
dschulze@6300 | 1368 | the <span class="term">transformation matrix</span>, as described above. |
simon@4366 | 1369 | </p> |
simon@4345 | 1370 | |
simon@4345 | 1371 | <!-- ======================================================================================================= --> |
simon@4345 | 1372 | |
simon@4345 | 1373 | <h2 id="perspective-property"> |
dschulze@6300 | 1374 | The 'perspective' Property |
simon@4345 | 1375 | </h2> |
simon@4345 | 1376 | <table class="propdef"> |
simon@4345 | 1377 | <tbody> |
simon@4345 | 1378 | <tr> |
simon@4345 | 1379 | <td> |
simon@4345 | 1380 | <em>Name:</em> |
simon@4345 | 1381 | </td> |
simon@4345 | 1382 | <td> |
simon@4345 | 1383 | <dfn id="perspective">perspective</dfn> |
simon@4345 | 1384 | </td> |
simon@4345 | 1385 | </tr> |
simon@4345 | 1386 | <tr> |
simon@4345 | 1387 | <td> |
simon@4345 | 1388 | <em>Value:</em> |
simon@4345 | 1389 | </td> |
simon@4345 | 1390 | <td> |
simon@4345 | 1391 | none | <length> |
simon@4345 | 1392 | </td> |
simon@4345 | 1393 | </tr> |
simon@4345 | 1394 | <tr> |
simon@4345 | 1395 | <td> |
simon@4345 | 1396 | <em>Initial:</em> |
simon@4345 | 1397 | </td> |
simon@4345 | 1398 | <td> |
simon@4345 | 1399 | none |
simon@4345 | 1400 | </td> |
simon@4345 | 1401 | </tr> |
simon@4345 | 1402 | <tr> |
simon@4345 | 1403 | <td> |
dbaron@7338 | 1404 | <em>Applies to:</em> |
simon@4345 | 1405 | </td> |
simon@4345 | 1406 | <td> |
simon@4345 | 1407 | <a href="#TermTransformableElement">transformable elements</a> |
simon@4345 | 1408 | </td> |
simon@4345 | 1409 | </tr> |
simon@4345 | 1410 | <tr> |
simon@4345 | 1411 | <td> |
simon@4345 | 1412 | <em>Inherited:</em> |
simon@4345 | 1413 | </td> |
simon@4345 | 1414 | <td> |
simon@4345 | 1415 | no |
simon@4345 | 1416 | </td> |
simon@4345 | 1417 | </tr> |
simon@4345 | 1418 | <tr> |
simon@4345 | 1419 | <td> |
simon@4345 | 1420 | <em>Percentages:</em> |
simon@4345 | 1421 | </td> |
simon@4345 | 1422 | <td> |
simon@4345 | 1423 | N/A |
simon@4345 | 1424 | </td> |
simon@4345 | 1425 | </tr> |
simon@4345 | 1426 | <tr> |
simon@4345 | 1427 | <td> |
simon@4345 | 1428 | <em>Media:</em> |
simon@4345 | 1429 | </td> |
simon@4345 | 1430 | <td> |
simon@4345 | 1431 | visual |
simon@4345 | 1432 | </td> |
simon@4345 | 1433 | </tr> |
simon@4345 | 1434 | <tr> |
simon@4345 | 1435 | <td> |
simon@4345 | 1436 | <em>Computed value:</em> |
simon@4345 | 1437 | </td> |
simon@4345 | 1438 | <td> |
ayg@4741 | 1439 | Absolute length or "none". |
simon@4345 | 1440 | </td> |
simon@4345 | 1441 | </tr> |
dbaron@7349 | 1442 | <tr> |
dbaron@7349 | 1443 | <td> |
dbaron@7349 | 1444 | <em>Animatable:</em> |
dbaron@7349 | 1445 | </td> |
dbaron@7349 | 1446 | <td> |
dbaron@7349 | 1447 | as <a href="http://dev.w3.org/csswg/css3-transitions/#animtype-length">length</a> |
dbaron@7349 | 1448 | </td> |
dbaron@7349 | 1449 | </tr> |
simon@4345 | 1450 | </tbody> |
simon@4345 | 1451 | </table> |
simon@4345 | 1452 | |
simon@4345 | 1453 | <p> |
dschulze@6300 | 1454 | If the value is ''none'', no perspective |
ayg@4744 | 1455 | transform is applied. Lengths must be positive. |
simon@4345 | 1456 | </p> |
simon@4345 | 1457 | <p> |
dschulze@6300 | 1458 | The use of this property with any value other than ''none'' establishes a |
simon@4345 | 1459 | stacking context. It also establishes a containing block (somewhat |
dschulze@6300 | 1460 | similar to ''position: relative''), just like the 'transform' property does. |
simon@4345 | 1461 | </p> |
simon@4366 | 1462 | <p> |
dschulze@6300 | 1463 | The values of the 'perspective' and 'perspective-origin' properties are used to |
dschulze@6300 | 1464 | compute the <span class="term">perspective matrix</span>, as described above. |
simon@4366 | 1465 | </p> |
ayg@5129 | 1466 | |
simon@4345 | 1467 | <!-- ======================================================================================================= --> |
ayg@5129 | 1468 | |
simon@4345 | 1469 | <h2 id="perspective-origin-property"> |
dschulze@6300 | 1470 | The 'perspective-origin' Property |
simon@4345 | 1471 | </h2> |
simon@4345 | 1472 | <p> |
dschulze@6300 | 1473 | The 'perspective-origin' property establishes the origin for the |
dschulze@6300 | 1474 | <em>perspective</em> property. It effectively sets the X and Y position at which the |
dschulze@6300 | 1475 | viewer appears to be looking at the children of the element. |
simon@4345 | 1476 | </p> |
simon@4345 | 1477 | <table class="propdef"> |
simon@4345 | 1478 | <tbody> |
simon@4345 | 1479 | <tr> |
simon@4345 | 1480 | <td> |
simon@4345 | 1481 | <em>Name:</em> |
simon@4345 | 1482 | </td> |
simon@4345 | 1483 | <td> |
simon@4345 | 1484 | <dfn id="perspective-origin">perspective-origin</dfn> |
simon@4345 | 1485 | </td> |
simon@4345 | 1486 | </tr> |
simon@4345 | 1487 | <tr> |
simon@4345 | 1488 | <td> |
simon@4345 | 1489 | <em>Value:</em> |
simon@4345 | 1490 | </td> |
simon@4345 | 1491 | <td> |
dschulze@7808 | 1492 | [ left | center | right | top | bottom | <var><percentage></var> | <var><length></var> ]<br> |
dschulze@7808 | 1493 | |<br> |
dschulze@7808 | 1494 | [ left | center | right | <var><percentage></var> | <var><length></var> ]<br> |
dschulze@7808 | 1495 | [ top | center | bottom | <var><percentage></var> | <var><length></var> ] |
simon@4345 | 1496 | </td> |
simon@4345 | 1497 | </tr> |
simon@4345 | 1498 | <tr> |
simon@4345 | 1499 | <td> |
simon@4345 | 1500 | <em>Initial:</em> |
simon@4345 | 1501 | </td> |
simon@4345 | 1502 | <td> |
simon@4345 | 1503 | 50% 50% |
simon@4345 | 1504 | </td> |
simon@4345 | 1505 | </tr> |
simon@4345 | 1506 | <tr> |
simon@4345 | 1507 | <td> |
dbaron@7338 | 1508 | <em>Applies to:</em> |
simon@4345 | 1509 | </td> |
simon@4345 | 1510 | <td> |
simon@4345 | 1511 | <a href="#TermTransformableElement">transformable elements</a> |
simon@4345 | 1512 | </td> |
simon@4345 | 1513 | </tr> |
simon@4345 | 1514 | <tr> |
simon@4345 | 1515 | <td> |
simon@4345 | 1516 | <em>Inherited:</em> |
simon@4345 | 1517 | </td> |
simon@4345 | 1518 | <td> |
simon@4345 | 1519 | no |
simon@4345 | 1520 | </td> |
simon@4345 | 1521 | </tr> |
simon@4345 | 1522 | <tr> |
simon@4345 | 1523 | <td> |
simon@4345 | 1524 | <em>Percentages:</em> |
simon@4345 | 1525 | </td> |
simon@4345 | 1526 | <td> |
dschulze@7030 | 1527 | refer to the size of the |
dschulze@7030 | 1528 | <var>bounding box</var> |
simon@4345 | 1529 | </td> |
simon@4345 | 1530 | </tr> |
simon@4345 | 1531 | <tr> |
simon@4345 | 1532 | <td> |
simon@4345 | 1533 | <em>Media:</em> |
simon@4345 | 1534 | </td> |
simon@4345 | 1535 | <td> |
simon@4345 | 1536 | visual |
simon@4345 | 1537 | </td> |
simon@4345 | 1538 | </tr> |
simon@4345 | 1539 | <tr> |
simon@4345 | 1540 | <td> |
simon@4345 | 1541 | <em>Computed value:</em> |
simon@4345 | 1542 | </td> |
simon@4345 | 1543 | <td> |
ayg@4741 | 1544 | For <length> the absolute value, otherwise a |
ayg@4741 | 1545 | percentage. |
simon@4345 | 1546 | </td> |
simon@4345 | 1547 | </tr> |
dbaron@7349 | 1548 | <tr> |
dbaron@7349 | 1549 | <td> |
dbaron@7349 | 1550 | <em>Animatable:</em> |
dbaron@7349 | 1551 | </td> |
dbaron@7349 | 1552 | <td> |
dbaron@7349 | 1553 | as <a href="http://dev.w3.org/csswg/css3-transitions/#animtype-simple-list">simple list</a> of <a href="http://dev.w3.org/csswg/css3-transitions/#animtype-lpcalc">length, percentage, or calc</a> |
dbaron@7349 | 1554 | </td> |
dbaron@7349 | 1555 | </tr> |
simon@4345 | 1556 | </tbody> |
simon@4345 | 1557 | </table> |
simon@4365 | 1558 | <p> |
dschulze@6300 | 1559 | The values of the 'perspective' and 'perspective-origin' properties are used to |
dschulze@6300 | 1560 | compute the <span class="term">perspective matrix</span>, as described above. |
simon@4365 | 1561 | </p> |
ayg@4737 | 1562 | |
dschulze@6300 | 1563 | <p> |
dschulze@6300 | 1564 | If only one value is specified, the second value is assumed to be ''center''. |
ayg@4743 | 1565 | </p> |
ayg@4743 | 1566 | |
dschulze@6300 | 1567 | <p> |
dschulze@6300 | 1568 | If at least one of the two values is not a keyword, then the |
dschulze@6300 | 1569 | first value represents the horizontal position (or offset) and |
dschulze@6300 | 1570 | the second represents the vertical position (or offset). |
ayg@4743 | 1571 | </p> |
ayg@4743 | 1572 | |
dschulze@6300 | 1573 | <p> |
dschulze@7030 | 1574 | The values for 'perspective-origin' represent an offset of the perspective origin |
dschulze@7030 | 1575 | from the top |
dschulze@7030 | 1576 | left corner of the <var>bounding box</var>. |
ayg@4743 | 1577 | </p> |
ayg@4743 | 1578 | |
dschulze@7030 | 1579 | <dl> |
dschulze@7030 | 1580 | <dt><var><percentage></var> |
dschulze@7030 | 1581 | |
dschulze@7030 | 1582 | <dd><p>A percentage for the horizontal perspctive offset is relative to the width of the |
dschulze@7030 | 1583 | <var>bounding box</var>. |
dschulze@7030 | 1584 | A percentage for the vertical offset is relative to height of the |
dschulze@7030 | 1585 | <var>bounding box</var>. The value for the horizontal |
dschulze@7030 | 1586 | and vertical offset represent an offset from the top left corner of the |
dschulze@7030 | 1587 | <var>bounding box</var>. |
dschulze@7030 | 1588 | </p> |
dschulze@7030 | 1589 | |
dschulze@7030 | 1590 | <dt><var><length></var> |
dschulze@7030 | 1591 | |
dschulze@7030 | 1592 | <dd> |
dschulze@7030 | 1593 | <p>A length value gives a fixed length as the offset. The value for the horizontal |
dschulze@7030 | 1594 | and vertical offset represent an offset from the top left corner of the |
dschulze@7030 | 1595 | <var>bounding box</var>. |
dschulze@7030 | 1596 | </dd> |
dschulze@7030 | 1597 | |
dschulze@7030 | 1598 | <dt><dfn title="''top''!!'perspective-origin' value">''top''</dfn> |
dschulze@7030 | 1599 | |
dschulze@7030 | 1600 | <dd>Computes to ''0%'' for the vertical position. |
dschulze@7030 | 1601 | |
dschulze@7030 | 1602 | <dt><dfn title="''right''!!'perspective-origin' value">''right''</dfn> |
dschulze@7030 | 1603 | |
dschulze@7030 | 1604 | <dd>Computes to ''100%'' for the horizontal position. |
dschulze@7030 | 1605 | |
dschulze@7030 | 1606 | <dt><dfn title="''bottom''!!'perspective-origin' value">''bottom''</dfn> |
dschulze@7030 | 1607 | |
dschulze@7030 | 1608 | <dd>Computes to ''100%'' for the vertical position. |
dschulze@7030 | 1609 | |
dschulze@7030 | 1610 | <dt><dfn title="''left''!!'perspective-origin' value">''left''</dfn> |
dschulze@7030 | 1611 | |
dschulze@7030 | 1612 | <dd>Computes to ''0%'' for the horizontal position. |
dschulze@7030 | 1613 | |
dschulze@7030 | 1614 | <dt><dfn title="''center''!!'perspective-origin' value">''center''</dfn> |
dschulze@7030 | 1615 | |
dschulze@7030 | 1616 | <dd>Computes to ''50%'' (''left 50%'') for the |
dschulze@7030 | 1617 | horizontal position if the horizontal position is not otherwise |
dschulze@7030 | 1618 | specified, or ''50%'' (''top 50%'') for the vertical position if |
dschulze@7030 | 1619 | it is. |
dschulze@7030 | 1620 | </dl> |
dschulze@7030 | 1621 | |
ayg@4737 | 1622 | <p> |
bert@5324 | 1623 | The <a href="http://www.w3.org/TR/cssom/#resolved-value">resolved value</a> |
dschulze@6300 | 1624 | of 'perspective-origin' is the |
ayg@4737 | 1625 | <a href="http://www.w3.org/TR/CSS21/cascade.html#used-value">used value</a> |
ayg@4737 | 1626 | (i.e., percentages are resolved to absolute lengths). |
ayg@4737 | 1627 | </p> |
ayg@4737 | 1628 | |
simon@4345 | 1629 | <!-- ======================================================================================================= --> |
simon@4345 | 1630 | |
simon@4345 | 1631 | <h2 id="backface-visibility-property"> |
dschulze@6300 | 1632 | The 'backface-visibility' Property |
simon@4345 | 1633 | </h2> |
simon@4345 | 1634 | <p> |
dschulze@6300 | 1635 | The 'backface-visibility' property |
simon@4345 | 1636 | determines whether or not the "back" side of a transformed element is |
simon@4345 | 1637 | visible when facing the viewer. With an identity transform, the front |
simon@4345 | 1638 | side of an element faces the viewer. Applying a rotation about Y of 180 |
simon@4345 | 1639 | degrees (for instance) would cause the back side of the element to face |
simon@4345 | 1640 | the viewer. |
simon@4345 | 1641 | </p> |
dschulze@6893 | 1642 | <p class="note"> |
dschulze@6893 | 1643 | Note that this property is useful when you place two elements back-to-back, as you |
simon@4345 | 1644 | would to create a playing card. Without this property, the front and |
simon@4345 | 1645 | back elements could switch places at times during an animation to flip |
simon@4345 | 1646 | the card. Another example is creating a box out of 6 elements, but where |
simon@4345 | 1647 | you want to see the inside faces of the box. This is useful when |
simon@4345 | 1648 | creating the backdrop for a 3 dimensional stage. |
simon@4345 | 1649 | </p> |
simon@4345 | 1650 | <table class="propdef"> |
simon@4345 | 1651 | <tbody> |
simon@4345 | 1652 | <tr> |
simon@4345 | 1653 | <td> |
simon@4345 | 1654 | <em>Name:</em> |
simon@4345 | 1655 | </td> |
simon@4345 | 1656 | <td> |
simon@4345 | 1657 | <dfn id="backface-visibility">backface-visibility</dfn> |
simon@4345 | 1658 | </td> |
simon@4345 | 1659 | </tr> |
simon@4345 | 1660 | <tr> |
simon@4345 | 1661 | <td> |
simon@4345 | 1662 | <em>Value:</em> |
simon@4345 | 1663 | </td> |
simon@4345 | 1664 | <td> |
simon@4345 | 1665 | visible | hidden |
simon@4345 | 1666 | </td> |
simon@4345 | 1667 | </tr> |
simon@4345 | 1668 | <tr> |
simon@4345 | 1669 | <td> |
simon@4345 | 1670 | <em>Initial:</em> |
simon@4345 | 1671 | </td> |
simon@4345 | 1672 | <td> |
simon@4345 | 1673 | visible |
simon@4345 | 1674 | </td> |
simon@4345 | 1675 | </tr> |
simon@4345 | 1676 | <tr> |
simon@4345 | 1677 | <td> |
dbaron@7338 | 1678 | <em>Applies to:</em> |
simon@4345 | 1679 | </td> |
simon@4345 | 1680 | <td> |
simon@4366 | 1681 | <a href="#TermTransformableElement">transformable elements</a> |
simon@4345 | 1682 | </td> |
simon@4345 | 1683 | </tr> |
simon@4345 | 1684 | <tr> |
simon@4345 | 1685 | <td> |
simon@4345 | 1686 | <em>Inherited:</em> |
simon@4345 | 1687 | </td> |
simon@4345 | 1688 | <td> |
simon@4345 | 1689 | no |
simon@4345 | 1690 | </td> |
simon@4345 | 1691 | </tr> |
simon@4345 | 1692 | <tr> |
simon@4345 | 1693 | <td> |
simon@4345 | 1694 | <em>Percentages:</em> |
simon@4345 | 1695 | </td> |
simon@4345 | 1696 | <td> |
simon@4345 | 1697 | N/A |
simon@4345 | 1698 | </td> |
simon@4345 | 1699 | </tr> |
simon@4345 | 1700 | <tr> |
simon@4345 | 1701 | <td> |
simon@4345 | 1702 | <em>Media:</em> |
simon@4345 | 1703 | </td> |
simon@4345 | 1704 | <td> |
simon@4345 | 1705 | visual |
simon@4345 | 1706 | </td> |
simon@4345 | 1707 | </tr> |
simon@4345 | 1708 | <tr> |
simon@4345 | 1709 | <td> |
simon@4345 | 1710 | <em>Computed value:</em> |
simon@4345 | 1711 | </td> |
simon@4345 | 1712 | <td> |
simon@4345 | 1713 | Same as specified value. |
simon@4345 | 1714 | </td> |
simon@4345 | 1715 | </tr> |
dbaron@7349 | 1716 | <tr> |
dbaron@7349 | 1717 | <td> |
dbaron@7349 | 1718 | <em>Animatable:</em> |
dbaron@7349 | 1719 | </td> |
dbaron@7349 | 1720 | <td> |
dbaron@7349 | 1721 | no |
dbaron@7349 | 1722 | </td> |
dbaron@7349 | 1723 | </tr> |
simon@4345 | 1724 | </tbody> |
simon@4345 | 1725 | </table> |
simon@4367 | 1726 | <p> |
dschulze@6300 | 1727 | The visibility of an element with ''backface-visibility: hidden'' is determined |
simon@4367 | 1728 | as follows: |
simon@4367 | 1729 | <ol> |
ayg@5127 | 1730 | <li> |
ayg@5127 | 1731 | For an element in a |
dschulze@5921 | 1732 | <span class="term">3D rendering context</span>, |
ayg@5127 | 1733 | compute its |
dschulze@5921 | 1734 | <span class="term"> |
dschulze@5921 | 1735 | accumulated 3D transformation matrix</span>. For an element |
ayg@5127 | 1736 | not in a |
dschulze@5921 | 1737 | <span class="term">3D rendering context</span>, |
ayg@5127 | 1738 | compute its |
dschulze@5921 | 1739 | <span class="term">transformation matrix</span>. |
simon@4367 | 1740 | </li> |
simon@4367 | 1741 | <li> |
ayg@5127 | 1742 | If the component of the matrix in row 3, column 3 is |
ayg@5127 | 1743 | negative, then the element should be hidden. Otherwise it |
ayg@5127 | 1744 | is visible. |
simon@4367 | 1745 | </li> |
simon@4367 | 1746 | </ol> |
bert@6704 | 1747 | |
ayg@5081 | 1748 | <p class="note"> |
ayg@5081 | 1749 | The reasoning for this definition is as follows. Assume |
ayg@5081 | 1750 | elements are rectangles in the <var>x</var>–<var>y</var> |
ayg@5081 | 1751 | plane with infinitesimal thickness. The front of the |
ayg@5081 | 1752 | untransformed element has coordinates like (<var>x</var>, |
ayg@5081 | 1753 | <var>y</var>, <var>ε</var>), and the back is |
ayg@5081 | 1754 | (<var>x</var>, <var>y</var>, −<var>ε</var>), for |
ayg@5081 | 1755 | some very small <var>ε</var>. We want to know if after |
ayg@5081 | 1756 | the transformation, the front of the element is closer to the |
ayg@5081 | 1757 | viewer than the back (higher <var>z</var>-value) or further |
ayg@5081 | 1758 | away. The <var>z</var>-coordinate of the front will be |
ayg@5081 | 1759 | <var>M</var><sub>13</sub><var>x</var> + |
ayg@5081 | 1760 | <var>M</var><sub>23</sub><var>y</var> + |
ayg@5081 | 1761 | <var>M</var><sub>33</sub><var>ε</var> + |
ayg@5081 | 1762 | <var>M</var><sub>43</sub>, before accounting for perspective, |
ayg@5081 | 1763 | and the back will be <var>M</var><sub>13</sub><var>x</var> + |
ayg@5081 | 1764 | <var>M</var><sub>23</sub><var>y</var> − |
ayg@5081 | 1765 | <var>M</var><sub>33</sub><var>ε</var> + |
ayg@5081 | 1766 | <var>M</var><sub>43</sub>. The first quantity is greater than |
ayg@5081 | 1767 | the second if and only if <var>M</var><sub>33</sub> > 0. (If |
ayg@5081 | 1768 | it equals zero, the front and back are equally close to the |
ayg@5081 | 1769 | viewer. This probably means something like a 90-degree |
ayg@5081 | 1770 | rotation, which makes the element invisible anyway, so we don't |
ayg@5081 | 1771 | really care whether it vanishes.) |
ayg@5081 | 1772 | </p> |
dschulze@5642 | 1773 | |
dschulze@5642 | 1774 | <!-- ======================================================================================================= --> |
dschulze@5642 | 1775 | |
dschulze@5642 | 1776 | <h2 id="svg-transform"> |
dschulze@5751 | 1777 | The SVG 'transform' Attribute |
dschulze@5642 | 1778 | </h2> |
dschulze@5642 | 1779 | |
dschulze@5642 | 1780 | <p> |
dschulze@5751 | 1781 | The <a href="http://www.w3.org/TR/2011/REC-SVG11-20110816/">SVG 1.1 |
dschulze@5751 | 1782 | specification</a> did not specify the attributes 'transform', 'gradientTransform' |
dschulze@5751 | 1783 | or 'patternTransform' as <a |
dschulze@5751 | 1784 | href="http://www.w3.org/TR/2011/REC-SVG11-20110816/styling.html#UsingPresentationAttributes"><em>presentation |
dschulze@5751 | 1785 | attributes</em></a>. In order to improve the integration of SVG and HTML, this |
dschulze@5751 | 1786 | specification makes these SVG attributes 'presentation attributes' and makes the |
dschulze@5751 | 1787 | 'transform' property one that applies to <span class="term">transformable |
dschulze@5895 | 1788 | elements</span> in the SVG namespace. |
dschulze@5642 | 1789 | </p> |
dschulze@5642 | 1790 | |
dschulze@5642 | 1791 | <p> |
dschulze@5751 | 1792 | This specification will also introduce the new presentation attributes |
dschulze@5751 | 1793 | 'transform-origin', 'perspective', 'perspective-origin', 'transform-style' and |
dschulze@5751 | 1794 | 'backface-visibility'. |
dschulze@5642 | 1795 | </p> |
dschulze@5642 | 1796 | |
dschulze@5642 | 1797 | <p> |
dschulze@5751 | 1798 | Values on new introduced presentation attributes get parsed following the syntax |
dschulze@5923 | 1799 | rules on <a href="#svg-data-types">SVG Data Types</a> [[SVG11]]. |
dschulze@5642 | 1800 | </p> |
dschulze@5642 | 1801 | |
dschulze@5751 | 1802 | <h3 id="transform-attribute-specificity"> |
dschulze@5751 | 1803 | SVG 'transform' attribute specificity |
dschulze@5751 | 1804 | </h3> |
dschulze@5642 | 1805 | |
dschulze@5751 | 1806 | <p> |
dschulze@5751 | 1807 | Since the previously named SVG attributes become presentation attributes, their |
dschulze@5751 | 1808 | participation in the CSS cascade is determined by the specificity of presentation |
dschulze@5751 | 1809 | attributes, as <a |
dschulze@5751 | 1810 | href="http://www.w3.org/TR/2011/REC-SVG11-20110816/styling.html#UsingPresentationAttributes">explained</a> |
dschulze@5751 | 1811 | in the SVG specification. |
dschulze@5642 | 1812 | </p> |
dschulze@5642 | 1813 | |
dschulze@5642 | 1814 | <div class="example"> |
dschulze@5642 | 1815 | <p> |
dschulze@5751 | 1816 | This example shows the combination of the 'transform' style property and the |
dschulze@5751 | 1817 | 'transform' presentation attribute. |
dschulze@5642 | 1818 | </p> |
dschulze@5642 | 1819 | |
dschulze@5642 | 1820 | <pre><svg xmlns="http://www.w3.org/2000/svg"> |
dschulze@5642 | 1821 | <style> |
dschulze@5642 | 1822 | .container { |
dschulze@5642 | 1823 | transform: translate(100px, 100px); |
dschulze@5642 | 1824 | } |
dschulze@5642 | 1825 | </style> |
dschulze@5642 | 1826 | |
dschulze@5642 | 1827 | <g class="container" transform="translate(200 200)"> |
dschulze@5642 | 1828 | <rect width="100" height="100" fill="blue" /> |
dschulze@5642 | 1829 | </g> |
dschulze@5642 | 1830 | </svg></pre> |
dschulze@5642 | 1831 | |
dschulze@5642 | 1832 | <div class="figure"> |
dschulze@5751 | 1833 | <img src="examples/svg-translate1.svg" width="470" height="240" |
dschulze@5751 | 1834 | alt="Translated SVG container element."> |
dschulze@5642 | 1835 | </div> |
dschulze@5642 | 1836 | |
dschulze@5642 | 1837 | <p> |
dschulze@5751 | 1838 | Because of the participation to the CSS cascade, the 'transform' style property |
dschulze@5751 | 1839 | overrides the 'transform' presentation attribute. Therefore the container gets |
dschulze@5751 | 1840 | translated by ''100px'' in both the horizontal and the vertical directions, |
dschulze@5751 | 1841 | instead of ''200px''. |
dschulze@5642 | 1842 | </p> |
dschulze@5642 | 1843 | </div> |
dschulze@5642 | 1844 | |
dschulze@5751 | 1845 | <h3 id="svg-syntax"> |
dschulze@5751 | 1846 | Syntax of the SVG 'transform' attribute |
dschulze@5751 | 1847 | </h3> |
dschulze@5642 | 1848 | |
dschulze@5642 | 1849 | <p> |
dschulze@5751 | 1850 | To provide backwards compatibility, the syntax of the 'transform' presentation |
dschulze@5751 | 1851 | attribute differs from the syntax of the 'transform' style property as shown in the |
dschulze@5751 | 1852 | example above. However, the syntax used for the 'transform' style property can be |
dschulze@5751 | 1853 | used for a 'transform' presentation attribute value. Authors are advised to follow |
dschulze@5751 | 1854 | the rules of <a href="http://www.w3.org/TR/css3-values/#functional-notation">CSS |
dschulze@5751 | 1855 | Values and Units Module</a>. Therefore an author should write |
dschulze@5751 | 1856 | ''transform="translate(200px, 200px)"'' instead of |
dschulze@5751 | 1857 | ''transform="translate (200 200)"'' because the second example with the spaces |
dschulze@5751 | 1858 | before the ''('', the missing comma between the arguments and the values without the |
dschulze@5751 | 1859 | explicit unit notation would be valid for the attribute only. |
dschulze@5642 | 1860 | </p> |
dschulze@5642 | 1861 | |
dschulze@5751 | 1862 | <h4 id="svg-transform-list"> |
dschulze@5751 | 1863 | Transform List |
dschulze@5751 | 1864 | </h4> |
dschulze@5642 | 1865 | |
dschulze@5642 | 1866 | <p> |
dschulze@5751 | 1867 | The value for the 'transform' attribute consists of a transform list with zero or |
dschulze@5751 | 1868 | more transform functions using <a href="#svg-functional-notation">functional |
dschulze@5751 | 1869 | notation</a>. If the transform list consists of more than one transform function, |
dschulze@5751 | 1870 | these functions are separated by optional whitespace, an optional comma ('','') |
dschulze@5751 | 1871 | and optional whitespace. The transform list can have optional whitespace characters |
dschulze@5751 | 1872 | before and after the list. |
dschulze@5642 | 1873 | </p> |
dschulze@5642 | 1874 | |
dschulze@5751 | 1875 | <h4 id="svg-functional-notation"> |
dschulze@5751 | 1876 | Functional Notation |
dschulze@5751 | 1877 | </h4> |
dschulze@5642 | 1878 | |
dschulze@5642 | 1879 | <p> |
dschulze@5751 | 1880 | The syntax starts with the name of the function followed by optional whitespace |
dschulze@5751 | 1881 | characters followed by a left parenthesis followed by optional whitespace followed |
dschulze@5751 | 1882 | by the argument(s) to the notation followed by optional whitespace followed by a |
dschulze@5751 | 1883 | right parenthesis. If a function takes more than one argument, the arguments are |
dschulze@5751 | 1884 | either separated by a comma ('','') with optional whitespace characters before and |
dschulze@5751 | 1885 | after the comma, or by one or more whitespace characters. |
dschulze@5642 | 1886 | </p> |
dschulze@5642 | 1887 | |
dschulze@5751 | 1888 | <h4 id="svg-data-types"> |
dschulze@5751 | 1889 | SVG Data Types |
dschulze@5751 | 1890 | </h4> |
dschulze@5642 | 1891 | |
dschulze@5642 | 1892 | <p> |
dschulze@5751 | 1893 | Arguments on all new introduced presentation attributes consist of data types in the |
dschulze@5751 | 1894 | sense of <a href="http://www.w3.org/TR/css3-values/#functional-notation">CSS Values |
dschulze@5751 | 1895 | and Units Module</a>. The definitions of data types in CSS Values and Units Module |
dschulze@5751 | 1896 | are enhanced as follows: |
dschulze@5642 | 1897 | </p> |
dschulze@5642 | 1898 | |
dschulze@5751 | 1899 | <h5 id="svg-transform-value"> |
dschulze@5751 | 1900 | The <var><translation-value></var> and <var><length></var> type |
dschulze@5751 | 1901 | </h5> |
dschulze@5642 | 1902 | |
dschulze@5642 | 1903 | <p> |
dschulze@5751 | 1904 | A translation-value or length can be a <var><number></var> without an unit |
dschulze@5751 | 1905 | identifier. In this case the <a href="#svg-number"><var>number</var></a> gets |
dschulze@5751 | 1906 | interpreted as "user unit". A user unit in the the <a |
dschulze@5751 | 1907 | href="http://www.w3.org/TR/2003/REC-SVG11-20030114/coords.html#InitialCoordinateSystem">initial |
dschulze@5751 | 1908 | coordinate system</a> is equivalent to the parent environment's notion of a pixel |
dschulze@5751 | 1909 | unit. |
dschulze@5642 | 1910 | </p> |
dschulze@5642 | 1911 | |
dschulze@5751 | 1912 | <h5 id="svg-angle"> |
dschulze@5751 | 1913 | The <var><angle></var> type |
dschulze@5751 | 1914 | </h5> |
dschulze@5642 | 1915 | |
dschulze@5642 | 1916 | <p> |
dschulze@5751 | 1917 | An angle can be a <var><number></var> without an unit identifier. In this case |
dschulze@5751 | 1918 | the <a href="#svg-number"><i>number</i></a> gets interpreted as a value in degrees. |
dschulze@5642 | 1919 | </p> |
dschulze@5642 | 1920 | |
dschulze@5751 | 1921 | <h5 id="svg-number"> |
dschulze@5751 | 1922 | The <var><number></var> type |
dschulze@5751 | 1923 | </h5> |
dschulze@5642 | 1924 | |
dschulze@5642 | 1925 | <p> |
dschulze@5751 | 1926 | SVG supports scientific notations for numbers. Therefore a <var>number</var> gets |
dschulze@5751 | 1927 | parsed like described in SVG <a |
dschulze@5751 | 1928 | href="http://www.w3.org/TR/SVG/types.html#DataTypeNumber">Basic data types</a> for |
dschulze@5751 | 1929 | SVG attributes. |
dschulze@5642 | 1930 | </p> |
dschulze@5642 | 1931 | |
dschulze@5751 | 1932 | <h3 id="svg-gradient-transform-pattern-transform"> |
dschulze@5751 | 1933 | The SVG 'gradientTransform' and 'patternTransform' attributes |
dschulze@5751 | 1934 | </h3> |
dschulze@5642 | 1935 | |
dschulze@5642 | 1936 | <p> |
dschulze@5751 | 1937 | SVG specifies the attributes 'gradientTransform' and 'patternTransform'. This |
dschulze@5751 | 1938 | specification makes both attributes presentation attributes. Both attributes use the |
dschulze@5751 | 1939 | same <a href="#svg-syntax">syntax</a> as the SVG 'transform' attribute. This |
dschulze@5751 | 1940 | specification does not introduce corresponding CSS style properties. Both, the |
dschulze@5751 | 1941 | 'gradientTransform' and the 'patternTransform' attribute, are presentation |
dschulze@5751 | 1942 | attributes for the 'transform' property. |
dschulze@5642 | 1943 | </p> |
dschulze@5642 | 1944 | |
dschulze@5658 | 1945 | <h3 id="svg-transform-functions"> |
dschulze@5658 | 1946 | SVG transform functions |
dschulze@5658 | 1947 | </h3> |
dschulze@5642 | 1948 | |
dschulze@5642 | 1949 | <p> |
dschulze@5658 | 1950 | For backwards compatibility with existing SVG content, this specification supports |
dschulze@5658 | 1951 | all transform functions defined by <a |
dschulze@6300 | 1952 | href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">The 'transform' |
dschulze@6300 | 1953 | attribute</a> in [[SVG11]]. Therefore the two-dimensional transform function |
dschulze@6300 | 1954 | ''rotate(<angle>)'' is extended as follows: |
dschulze@5642 | 1955 | </p> |
dschulze@5642 | 1956 | |
dschulze@5642 | 1957 | <dl> |
dschulze@5743 | 1958 | <dt id="rotate-three-function"> |
dschulze@5658 | 1959 | <code class="css">rotate(<angle>[, <translation-value>, |
dschulze@5658 | 1960 | <translation-value>])</code> |
dschulze@5642 | 1961 | </dt> |
dschulze@5642 | 1962 | <dd> |
dschulze@5658 | 1963 | specifies a <a href="#RotateDefined">2D rotation</a> by the angle specified in the |
dschulze@5658 | 1964 | parameter about the origin of the element, as defined by the 'transform-origin' |
dschulze@5658 | 1965 | property. If the optional translation values are specified, the transform origin |
dschulze@5658 | 1966 | is translated by that amount (using the current transformation matrix) for the |
dschulze@5921 | 1967 | duration of the rotate operation. For example ''rotate(90deg, 100px, 100px)'' |
dschulze@5921 | 1968 | would cause elements to appear rotated one-quarter of a turn in the clockwise |
dschulze@6299 | 1969 | direction after a translation of the transform-origin of 100 pixel in the |
dschulze@6299 | 1970 | horizontal and vertical directions. |
dschulze@5642 | 1971 | </dd> |
dschulze@5642 | 1972 | </dl> |
dschulze@5642 | 1973 | |
dschulze@5642 | 1974 | <p> |
dschulze@5658 | 1975 | User agents are just required to support the two optional arguments for translation |
dschulze@5658 | 1976 | on elements in the SVG namespace. |
dschulze@5642 | 1977 | </p> |
dschulze@5642 | 1978 | |
dschulze@5642 | 1979 | <h3 id="svg-three-dimensional-functions">SVG and 3D transform functions</h3> |
dschulze@5642 | 1980 | |
dschulze@5642 | 1981 | <p> |
dschulze@5751 | 1982 | This specification explicitly requires three-dimensional transform functions to |
dschulze@5751 | 1983 | apply to the <a |
dschulze@5751 | 1984 | href="http://www.w3.org/TR/SVG/intro.html#TermContainerElement"><em>container |
dschulze@5751 | 1985 | elements</em></a>: 'a', 'g', 'svg', all <a |
dschulze@5751 | 1986 | href="http://www.w3.org/TR/SVG/intro.html#TermGraphicsElement"><em>graphics |
dschulze@5751 | 1987 | elements</em></a>, all <a |
dschulze@5751 | 1988 | href="http://www.w3.org/TR/SVG/intro.html#TermGraphicsReferencingElement"><em>graphics |
dschulze@5751 | 1989 | referencing elements</em></a> and the SVG '<a |
dschulze@5751 | 1990 | href="http://www.w3.org/TR/SVG/extend.html#ForeignObjectElement">foreignObject</a>' |
dschulze@5751 | 1991 | element. |
dschulze@5642 | 1992 | </p> |
dschulze@5642 | 1993 | |
dschulze@5642 | 1994 | <p> |
dschulze@5751 | 1995 | Three-dimensional transform functions and the properties 'perspective', |
dschulze@5751 | 1996 | 'perspective-origin', 'transform-style' and 'backface-visibility' can not be used |
dschulze@6897 | 1997 | for the elements: 'clipPath', 'linearGradient', 'radialGradient' and |
dschulze@5751 | 1998 | 'pattern'. If a transform list includes a three-dimensional transform function, the |
dschulze@5751 | 1999 | complete transform list must be ignored. The values of every previously named |
dschulze@5751 | 2000 | property must be ignored. <span class="term">Transformable elements</span> that are |
dschulze@5751 | 2001 | contained by one of these elements can have three-dimensional transform functions. |
dschulze@5751 | 2002 | Before a 'clipPath', 'mask' or 'pattern' element can get applied to a target |
dschulze@5751 | 2003 | element, user agents must take the drawn results as static images in analogue of |
dschulze@5751 | 2004 | "flattening" the elements and taking the rendered content as a two-dimensional |
dschulze@5751 | 2005 | canvas. |
dschulze@5642 | 2006 | </p> |
dschulze@5909 | 2007 | |
dschulze@5909 | 2008 | <p> |
dschulze@5909 | 2009 | If the 'vector-effect' property is set to ''non-scaling-stroke'' and an object is |
dschulze@5921 | 2010 | within a <span class="term">3D rendering context</span> |
dschulze@5909 | 2011 | the property has no affect on stroking the object. |
dschulze@5909 | 2012 | </p> |
dschulze@5642 | 2013 | |
dschulze@5751 | 2014 | <h3 id="svg-user-coordinate-space"> |
dschulze@5751 | 2015 | User coordinate space |
dschulze@5751 | 2016 | </h3> |
dschulze@6286 | 2017 | |
dschulze@5642 | 2018 | <p> |
dschulze@6897 | 2019 | For the 'pattern', 'linearGradient', 'radialGradient' and 'clipPath' |
dschulze@6286 | 2020 | elements the 'transform', 'patternTransform', 'gradientTransform' presentation |
dschulze@6286 | 2021 | attributes represents values in the current user coordinate system in place at the |
dschulze@6286 | 2022 | time when these elements are referenced (i.e., the user coordinate system for the |
dschulze@6286 | 2023 | element referencing the 'pattern' element via a 'fill' or 'stroke' property). |
dschulze@6929 | 2024 | Percentage values are relative to the <span>bounding box</span> of the |
dschulze@6929 | 2025 | referencing element. |
dschulze@5642 | 2026 | </p> |
dschulze@5642 | 2027 | |
dschulze@5642 | 2028 | <p> |
dschulze@6286 | 2029 | In particualar the 'patternUnit', 'gradientUnit' and 'maskUnit' attributes |
dschulze@6286 | 2030 | don't affect the user coordinate system used for transformations [[SVG11]]. |
dschulze@5751 | 2031 | </p> |
dschulze@6286 | 2032 | |
dschulze@5751 | 2033 | <p> |
dschulze@5921 | 2034 | For all other <span class="term">transformable elements</span> the |
dschulze@5751 | 2035 | 'transform' presentation attribute represents values in the current user coordinate |
dschulze@5751 | 2036 | system of the parent. All percentage values of the 'transform' presentation |
dschulze@7030 | 2037 | attribute are relative to the element's <var>bounding box</var>. |
dschulze@5642 | 2038 | </p> |
dschulze@5642 | 2039 | |
dschulze@5642 | 2040 | <div class="example"> |
dschulze@5642 | 2041 | <p> |
dschulze@5751 | 2042 | The 'transform-origin' property on the pattern in the following example specifies |
dschulze@5751 | 2043 | a ''50%'' translation of the origin in the horizontal and vertical dimension. The |
dschulze@5751 | 2044 | 'transform' property specifies a translation as well, but in absolute lengths. |
dschulze@5642 | 2045 | </p> |
dschulze@5642 | 2046 | |
dschulze@5642 | 2047 | <pre><svg xmlns="http://www.w3.org/2000/svg"> |
dschulze@5642 | 2048 | <style> |
dschulze@5642 | 2049 | pattern { |
dschulze@5751 | 2050 | transform: rotate(45deg); |
dschulze@5642 | 2051 | transform-origin: 50% 50%; |
dschulze@5642 | 2052 | } |
dschulze@5642 | 2053 | </style> |
dschulze@5642 | 2054 | |
dschulze@5642 | 2055 | <defs> |
dschulze@5642 | 2056 | <pattern id="pattern-1"> |
dschulze@5642 | 2057 | <rect id="rect1" width="100" height="100" fill="blue" /> |
dschulze@5642 | 2058 | </pattern> |
dschulze@5642 | 2059 | </defs> |
dschulze@5642 | 2060 | |
dschulze@6929 | 2061 | <rect width="200" height="200" fill="url(#pattern-1)" /> |
dschulze@5642 | 2062 | </svg></pre> |
dschulze@5642 | 2063 | |
dschulze@5642 | 2064 | <p> |
dschulze@7030 | 2065 | An SVG 'pattern' element doesn't have a bounding box. The <var>bounding box</var> |
dschulze@7030 | 2066 | of the referencing 'rect' element is used |
dschulze@5921 | 2067 | instead to solve the relative values of the 'transform-origin' property. Therefore |
dschulze@6929 | 2068 | the point of origin will get translated by 100 pixels temporarily to rotate the |
dschulze@5921 | 2069 | user space of the 'pattern' elements content. |
dschulze@5642 | 2070 | </p> |
dschulze@5642 | 2071 | </div> |
dschulze@5642 | 2072 | |
dschulze@5751 | 2073 | <h3 id="transform-attribute-dom"> |
dschulze@5751 | 2074 | SVG DOM interface for the 'transform' attribute |
dschulze@5751 | 2075 | </h3> |
dschulze@5642 | 2076 | |
dschulze@5642 | 2077 | <p> |
dschulze@5751 | 2078 | The SVG specification defines the '<a |
dschulze@5751 | 2079 | href="http://www.w3.org/TR/2011/REC-SVG11-20110816/coords.html#InterfaceSVGAnimatedTransformList">SVGAnimatedTransformList</a>' |
dschulze@5751 | 2080 | interface in the SVG DOM to provide access to the animated and the base value of the |
dschulze@5751 | 2081 | SVG 'transform', 'gradientTransform' and 'patternTransform' attributes. To ensure |
dschulze@5751 | 2082 | backwards compatibility, this API must still be supported by user agents. |
dschulze@5642 | 2083 | </p> |
dschulze@5642 | 2084 | |
dschulze@5642 | 2085 | <p> |
dschulze@5751 | 2086 | The 'transform' property contributes to the CSS cascade. According to SVG 1.1 user |
dschulze@5751 | 2087 | agents conceptually insert a <a |
dschulze@5751 | 2088 | href="http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes">new author |
dschulze@5751 | 2089 | style sheet</a> for presentation attributes, which is the first in the author style |
dschulze@5751 | 2090 | sheet collection. 'baseVal' gives the author the possibility to access and modify |
dschulze@5751 | 2091 | the values of the SVG 'transform' attribute. To provide the necessary backwards |
dschulze@5751 | 2092 | compatibility to the SVG DOM, 'baseVal' must reflect the values of this author style |
dschulze@5751 | 2093 | sheet. All modifications to SVG DOM objects of 'baseVal' must affect this author |
dschulze@5751 | 2094 | style sheet immediately. |
dschulze@5642 | 2095 | </p> |
dschulze@5642 | 2096 | |
dschulze@5642 | 2097 | <p> |
dschulze@5751 | 2098 | 'animVal' represents the computed style of the 'transform' property. Therefore it |
dschulze@5751 | 2099 | includes all applied <a |
dschulze@5751 | 2100 | href="http://www.w3.org/TR/css3-transitions/">CSS3 Transitions</a>, <a |
dschulze@5751 | 2101 | href="http://www.w3.org/TR/css3-animations/">CSS3 Animations</a> or <a |
dschulze@5751 | 2102 | href="#svg-animation">SVG Animations</a> if any of those are underway. The computed |
dschulze@5751 | 2103 | style and SVG DOM objects of 'animVal' can not be modified. |
dschulze@5642 | 2104 | </p> |
dschulze@5642 | 2105 | |
dschulze@5642 | 2106 | <p> |
dschulze@5751 | 2107 | The attribute '<a |
dschulze@5751 | 2108 | href="http://www.w3.org/TR/SVG/coords.html#__svg__SVGTransform__type">type</a>' of |
dschulze@5751 | 2109 | '<a |
dschulze@5751 | 2110 | href="http://www.w3.org/TR/SVG/coords.html#InterfaceSVGTransform">SVGTransform</a>' |
dschulze@5751 | 2111 | must return '<a |
dschulze@5921 | 2112 | href="http://www.w3.org/TR/SVG/coords.html#__svg__SVGTransform__SVG_TRANSFORM_UNKNOWN">SVG_TRANSFORM_UNKNOWN</a>' |
dschulze@5921 | 2113 | for <a href="#transform-functions">Transform Functions</a> |
dschulze@5751 | 2114 | or unit types that are not supported by this interface. If a two-dimensional |
dschulze@5751 | 2115 | transform function is not supported, the attribute '<a |
dschulze@5751 | 2116 | href="http://www.w3.org/TR/SVG/coords.html#__svg__SVGTransform__matrix">matrix</a>' |
dschulze@5751 | 2117 | must return a 3x2 '<a |
dschulze@5751 | 2118 | href="http://www.w3.org/TR/SVG/coords.html#InterfaceSVGMatrix">SVGMatrix</a>' with |
dschulze@5751 | 2119 | the corresponding values as described in the section <a |
dschulze@5751 | 2120 | href="#mathematical-description">Mathematical Description of Transform Functions</a>. |
dschulze@5642 | 2121 | </p> |
dschulze@5642 | 2122 | |
dschulze@5895 | 2123 | <h2 id="svg-animation"> |
dschulze@5751 | 2124 | SVG Animation |
dschulze@5895 | 2125 | </h2> |
dschulze@5895 | 2126 | |
dschulze@5895 | 2127 | <h3 id="svg-animate-element"> |
dschulze@5921 | 2128 | The 'animate' and 'set' element |
dschulze@5751 | 2129 | </h3> |
dschulze@5895 | 2130 | |
dschulze@5921 | 2131 | <p> |
dschulze@5921 | 2132 | With this specification, the 'animate' element and the 'set' element can animate |
dschulze@5921 | 2133 | the data type <var><transform-list></var>. |
dschulze@5921 | 2134 | </p> |
dschulze@5921 | 2135 | |
dschulze@5921 | 2136 | <p> |
dschulze@5921 | 2137 | The animation effect is post-multiplied to the underlying |
dschulze@5921 | 2138 | value for additive 'animate' animations (see below) instead of added to the |
dschulze@5921 | 2139 | underlying value, due to the specific behavior of <var><transform-list></var> |
dschulze@5921 | 2140 | animations. |
dschulze@5921 | 2141 | </p> |
dschulze@5921 | 2142 | |
dschulze@5921 | 2143 | <p> |
dschulze@5921 | 2144 | <var>From-to</var>, <var>from-by</var> and <var>by</var> animations are defined in |
dschulze@5921 | 2145 | SMIL to be equivalent to a corresponding <var>values</var> animation. However, |
dschulze@5921 | 2146 | <var>to</var> animations are a mixture of additive and non-additive behavior |
dschulze@5921 | 2147 | [[SMIL3]]. |
dschulze@5921 | 2148 | </p> |
dschulze@5921 | 2149 | |
dschulze@5921 | 2150 | <p> |
dschulze@5921 | 2151 | <var>To</var> animations on 'animate' provide specific functionality to get a smooth |
dschulze@5921 | 2152 | change from the underlying value to the 'to' attribute value, which conflicts |
dschulze@5921 | 2153 | mathematically with the requirement for additive transform animations to be |
dschulze@5921 | 2154 | post-multiplied. As a consequence, the behavior of <var>to</var> animations for |
dschulze@5921 | 2155 | 'animate' is undefined. Authors are suggested to use <var>from-to</var>, |
dschulze@5921 | 2156 | <var>from-by</var>, <var>by</var> or <var>values</var> animations to achieve any |
dschulze@5921 | 2157 | desired transform animation. |
dschulze@5921 | 2158 | </p> |
dschulze@5921 | 2159 | |
dschulze@5921 | 2160 | <p> |
dschulze@5921 | 2161 | The value 'paced' is undefined for the attribute 'calcMode' on 'animate' for animations of the |
dschulze@5921 | 2162 | data type <var><transform-list></var>. If specified, |
dschulze@5921 | 2163 | UAs may choose the value 'linear' instead. Future versions of this specification |
dschulze@5921 | 2164 | may define how paced animations can be performed on |
dschulze@5921 | 2165 | <var><transform-list></var>. |
dschulze@5921 | 2166 | </p> |
dschulze@5921 | 2167 | |
dschulze@5921 | 2168 | <p class="note">The following paragraphs extend <a |
dschulze@5895 | 2169 | href="http://www.w3.org/TR/SVG/animate.html#complexDistances">Elements, attributes |
dschulze@5895 | 2170 | and properties that can be animated</a> [[SVG11]]. |
dschulze@5895 | 2171 | </p> |
dschulze@5895 | 2172 | |
dschulze@5895 | 2173 | <p> |
dschulze@5895 | 2174 | The introduce presentation attributes 'transform', 'transform-origin', 'perspective', |
dschulze@5895 | 2175 | 'perspective-origin', 'transform-style' and 'backface-visibility' are animatable. |
dschulze@5895 | 2176 | 'transform-style' and 'backface-visibility' are non-additive. |
dschulze@5895 | 2177 | </p> |
dschulze@5642 | 2178 | |
dschulze@5642 | 2179 | <p> |
dschulze@5895 | 2180 | With this specification the SVG basic data type <var><transform-list></var> is |
dschulze@5895 | 2181 | equivalent to a list of <var><transform-function></var>s. |
dschulze@5895 | 2182 | <var><transform-list></var> is animatable and additive. The data type can be |
dschulze@5895 | 2183 | animated using the SVG '<a |
dschulze@5895 | 2184 | href="http://www.w3.org/TR/SVG/animate.html#AnimateElement">animate</a>' element and |
dschulze@5751 | 2185 | the SVG '<a href="http://www.w3.org/TR/SVG/animate.html#SetElement">set</a>' |
dschulze@5895 | 2186 | element. SVG animations must run the same animation steps as described in section <a |
dschulze@5895 | 2187 | href="#animation">Transitions and Animations between Transform Values</a>. |
dschulze@5895 | 2188 | </p> |
dschulze@5895 | 2189 | |
dschulze@5895 | 2190 | <p> |
dschulze@5895 | 2191 | The set of animatable data types gets extended by <em><translation-value></em>. |
dschulze@5895 | 2192 | The new data type is animatable and additive. |
dschulze@5642 | 2193 | </p> |
dschulze@5642 | 2194 | |
dschulze@5895 | 2195 | <table class="data"> |
dschulze@5895 | 2196 | <caption>Animatable data types</caption> |
dschulze@5895 | 2197 | <thead> |
dschulze@5895 | 2198 | <tr> |
dschulze@5895 | 2199 | <th>Data type</th> |
dschulze@5895 | 2200 | <th>Additive?</th> |
dschulze@5895 | 2201 | <th>'animate'</th> |
dschulze@5895 | 2202 | <th>'set'</th> |
dschulze@5895 | 2203 | <th>'animateColor'</th> |
dschulze@5895 | 2204 | <th>'animateTransform'</th> |
dschulze@5895 | 2205 | <th>Notes</th> |
dschulze@5895 | 2206 | </tr> |
dschulze@5895 | 2207 | </thead> |
dschulze@5895 | 2208 | <tbody> |
dschulze@5895 | 2209 | <tr> |
dschulze@5902 | 2210 | <th><var><transform-list></var></th> |
dschulze@5895 | 2211 | <td>yes</td> |
dschulze@5895 | 2212 | <td>yes</td> |
dschulze@5895 | 2213 | <td>yes</td> |
dschulze@5895 | 2214 | <td>no</td> |
dschulze@5895 | 2215 | <td>yes</td> |
dschulze@5902 | 2216 | <td>Additive for 'animateTransform' means that a transformation is post-multiplied to the base set of |
dschulze@5900 | 2217 | transformations.</td> |
dschulze@5895 | 2218 | </tr> |
dschulze@5895 | 2219 | <tr> |
dschulze@5902 | 2220 | <th><var><translation-value></var></th> |
dschulze@5895 | 2221 | <td>yes</td> |
dschulze@5895 | 2222 | <td>yes</td> |
dschulze@5895 | 2223 | <td>yes</td> |
dschulze@5895 | 2224 | <td>no</td> |
dschulze@5895 | 2225 | <td>no</td> |
dschulze@5895 | 2226 | <td> </td> |
dschulze@5895 | 2227 | </tr> |
dschulze@5895 | 2228 | </tbody> |
dschulze@5895 | 2229 | </table> |
dschulze@5900 | 2230 | |
dschulze@5900 | 2231 | <h3 id="neutral-element"> |
dschulze@5900 | 2232 | Neutral element for addition |
dschulze@5900 | 2233 | </h3> |
dschulze@5900 | 2234 | |
dschulze@5900 | 2235 | <p> |
dschulze@5921 | 2236 | Some animations require a neutral element for addition. For transform functions |
dschulze@5921 | 2237 | this is a scalar or a list of scalars of 0. Examples of neutral elements for |
dschulze@5921 | 2238 | transform functions are ''translate(0)'', ''translate3d(0, 0, 0)'', |
dschulze@5921 | 2239 | ''translateX(0)'', ''translateY(0)'', ''translateZ(0)'', ''scale(0)'', |
dschulze@5921 | 2240 | ''scaleX(0)'', ''scaleY(0)'', ''scaleZ(0)'', ''rotate(0)'', |
dschulze@5921 | 2241 | ''rotate3d(v<sub>x</sub>, v<sub>y</sub>, v<sub>z</sub>, 0)'' (where <var>v</var> is |
dschulze@5921 | 2242 | a context dependent vector), ''rotateX(0)'', ''rotateY(0)'', ''rotateZ(0)'', |
dschulze@6902 | 2243 | ''skew(0, 0)'', ''skewX(0)'', ''skewY(0)'', ''matrix(0, 0, 0, 0, 0, 0)'', ''matrix3d(0, 0, 0, 0, 0, |
dschulze@5921 | 2244 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)'' and ''perspective(0)''. |
dschulze@5921 | 2245 | </p> |
dschulze@5921 | 2246 | |
dschulze@5921 | 2247 | <p class="note"> |
dschulze@5923 | 2248 | Animations to or from the neutral element of additions ''matrix'', ''matrix3d'' and |
dschulze@5923 | 2249 | ''perspective'' fall back to discrete animations (See <a |
dschulze@5923 | 2250 | href="#matrix-interpolation">Interpolation of Matrices</a>). |
dschulze@5900 | 2251 | </p> |
dschulze@5900 | 2252 | |
dschulze@5900 | 2253 | <div class="example"> |
dschulze@5900 | 2254 | <p> |
dschulze@5900 | 2255 | A <var>by</var> animation with a by value v<sub>b</sub> is equivalent to the same |
dschulze@5900 | 2256 | animation with a values list with 2 values, the neutral element for addition for |
dschulze@5900 | 2257 | the domain of the target attribute (denoted 0) and v<sub>b</sub>, and |
dschulze@5921 | 2258 | ''additive="sum"''. [[SMIL3]] |
dschulze@5900 | 2259 | </p> |
dschulze@5900 | 2260 | |
dschulze@5900 | 2261 | <pre> |
dschulze@5900 | 2262 | <rect width="100" height="100"> |
dschulze@5900 | 2263 | <animateTransform attributeName="transform" attributeType="XML" |
dschulze@5900 | 2264 | type="scale" by="1" dur="5s" fill="freeze"/> |
dschulze@5900 | 2265 | </rect></pre> |
dschulze@5900 | 2266 | |
dschulze@5900 | 2267 | <p> |
dschulze@5900 | 2268 | The neutral element for addition when performing a <var>by</var> animation with |
dschulze@5900 | 2269 | ''type="scale"'' is the value 0. Thus, performing the animation of the example |
dschulze@5900 | 2270 | above causes the rectangle to be invisible at time 0s (since the animated |
dschulze@5900 | 2271 | transform list value is ''scale(0)''), and be scaled back to its original size at |
dschulze@5900 | 2272 | time 5s (since the animated transform list value is ''scale(1)''). |
dschulze@5900 | 2273 | </p> |
dschulze@5900 | 2274 | |
dschulze@5900 | 2275 | </div> |
dschulze@5895 | 2276 | |
dschulze@5895 | 2277 | <h3 id="svg-attribute-name"> |
dschulze@5751 | 2278 | The SVG 'attributeName' attribute |
dschulze@5895 | 2279 | </h3> |
dschulze@5642 | 2280 | |
dschulze@5642 | 2281 | <p> |
dschulze@5751 | 2282 | <a href="http://www.w3.org/TR/SVG/animate.html">SVG 1.1 Animation</a> defines the |
dschulze@5751 | 2283 | '<a href="http://www.w3.org/TR/SVG/animate.html#TargetAttributes">attributeName</a>' |
dschulze@5751 | 2284 | attribute to specify the name of the target attribute. For the presentation |
dschulze@5751 | 2285 | attributes 'gradientTransform' and 'patternTransform' it will also be possible to |
dschulze@5751 | 2286 | use the value 'transform'. The same 'transform' property will get animated. |
dschulze@5642 | 2287 | </p> |
dschulze@5642 | 2288 | |
dschulze@5642 | 2289 | <div class="example"> |
dschulze@5642 | 2290 | <p> |
dschulze@5642 | 2291 | In this example the gradient transformation of the linear gradient gets animated. |
dschulze@5642 | 2292 | </p> |
dschulze@5642 | 2293 | |
dschulze@5642 | 2294 | <pre><linearGradient gradientTransform="scale(2)"> |
dschulze@5642 | 2295 | <animate attributeName="gradientTransform" from="scale(2)" to="scale(4)" |
dschulze@5642 | 2296 | dur="3s" additive="sum"/> |
dschulze@5642 | 2297 | <animate attributeName="transform" from="translate(0, 0)" to="translate(100px, 100px)" |
dschulze@5642 | 2298 | dur="3s" additive="sum"/> |
dschulze@5642 | 2299 | </linearGradient></pre> |
dschulze@5642 | 2300 | |
dschulze@5751 | 2301 | <p>The 'linearGradient' element specifies the 'gradientTransform' presentation |
dschulze@5751 | 2302 | attribute. The two 'animate' elements address the target attribute |
dschulze@5751 | 2303 | 'gradientTransform' and 'transform'. Even so all animations apply to the same |
dschulze@5751 | 2304 | gradient transformation by taking the value of the 'gradientTransform' |
dschulze@5751 | 2305 | presentation attribute, applying the scaling of the first animation and applying |
dschulze@5751 | 2306 | the translation of the second animation one after the other. |
dschulze@5751 | 2307 | </p> |
dschulze@5642 | 2308 | </div> |
dschulze@5642 | 2309 | |
vhardy@3672 | 2310 | <!-- ======================================================================================================= --> |
vhardy@3672 | 2311 | |
vhardy@3672 | 2312 | <h2 id="transform-functions"> |
dschulze@5114 | 2313 | The Transform Functions |
vhardy@3672 | 2314 | </h2> |
vhardy@3672 | 2315 | <p> |
dschulze@5743 | 2316 | The value of the 'transform' property is a list of |
dschulze@5743 | 2317 | <var><transform-functions></var>. The set of allowed transform functions is |
dschulze@5743 | 2318 | given below. For <var><transform-functions></var> the type |
dschulze@5743 | 2319 | <var><translation-value></var> is defined as a <var><length></var> or |
dschulze@5743 | 2320 | <var><percentage></var> value, and the <var><angle></var> type is |
dschulze@5743 | 2321 | defined by <a |
dschulze@5743 | 2322 | href="http://www.w3.org/TR/css3-values/">CSS Values and Units Module.</a> Wherever |
dschulze@5743 | 2323 | <var><angle></var> is used in this specification, a <var><number></var> |
dschulze@5743 | 2324 | that is equal to zero is also allowed, which is treated the same as an angle of zero |
dschulze@5743 | 2325 | degrees. |
vhardy@3672 | 2326 | </p> |
simon@4358 | 2327 | |
dschulze@5114 | 2328 | <h3 id="two-d-transform-functions">2D Transform Functions</h3> |
vhardy@3672 | 2329 | <dl> |
dschulze@5743 | 2330 | <dt id="matrix-function"> |
simon@4365 | 2331 | <code class="css">matrix(<number>, <number>, <number>, <number>, <number>, <number>)</code> |
vhardy@3672 | 2332 | </dt> |
vhardy@3672 | 2333 | <dd> |
dschulze@4491 | 2334 | specifies a 2D transformation in the form of a <a href="#MatrixDefined">transformation matrix</a> of the six values a-f. |
vhardy@3672 | 2335 | </dd> |
dschulze@5743 | 2336 | <dt id="translate-function"> |
simon@4365 | 2337 | <code class="css">translate(<translation-value>[, <translation-value>])</code> |
vhardy@3672 | 2338 | </dt> |
vhardy@3672 | 2339 | <dd> |
dschulze@4491 | 2340 | specifies a <a href="#TranslateDefined">2D translation</a> by the vector [tx, ty], where tx is the first translation-value parameter and ty is the optional second translation-value parameter. If <em><ty></em> is not provided, ty has zero as a value. |
vhardy@3672 | 2341 | </dd> |
dschulze@5743 | 2342 | <dt id="translateX-function"> |
simon@4365 | 2343 | <code class="css">translateX(<translation-value>)</code> |
vhardy@3672 | 2344 | </dt> |
vhardy@3672 | 2345 | <dd> |
dschulze@4491 | 2346 | specifies a <a href="#TranslateDefined">translation</a> by the given amount in the X direction. |
vhardy@3672 | 2347 | </dd> |
dschulze@5743 | 2348 | <dt id="translateY-function"> |
simon@4365 | 2349 | <code class="css">translateY(<translation-value>)</code> |
vhardy@3672 | 2350 | </dt> |
vhardy@3672 | 2351 | <dd> |
dschulze@4491 | 2352 | specifies a <a href="#TranslateDefined">translation</a> by the given amount in the Y direction. |
vhardy@3672 | 2353 | </dd> |
dschulze@5743 | 2354 | <dt id="scale-function"> |
simon@4365 | 2355 | <code class="css">scale(<number>[, <number>])</code> |
vhardy@3672 | 2356 | </dt> |
vhardy@3672 | 2357 | <dd> |
dschulze@6607 | 2358 | specifies a <a href="#ScaleDefined">2D scale</a> operation by the [sx,sy] scaling vector described by the 2 parameters. If the second parameter is not provided, it takes a value equal to the first. For example, scale(1, 1) would leave an element unchanged, while scale(2, 2) would cause it to appear twice as long in both the X |
vhardy@3672 | 2359 | and Y axes, or four times its typical geometric size. |
vhardy@3672 | 2360 | </dd> |
dschulze@5743 | 2361 | <dt id="scaleX-function"> |
simon@4365 | 2362 | <code class="css">scaleX(<number>)</code> |
vhardy@3672 | 2363 | </dt> |
vhardy@3672 | 2364 | <dd> |
dschulze@4491 | 2365 | specifies a <a href="#ScaleDefined">2D scale</a> operation using the [sx,1] scaling vector, where sx is given as the parameter. |
vhardy@3672 | 2366 | </dd> |
dschulze@5743 | 2367 | <dt id="scaleY-function"> |
simon@4365 | 2368 | <code class="css">scaleY(<number>)</code> |
vhardy@3672 | 2369 | </dt> |
vhardy@3672 | 2370 | <dd> |
dschulze@4491 | 2371 | specifies a <a href="#ScaleDefined">2D scale</a> operation using the [1,sy] scaling vector, where sy is given as the parameter. |
vhardy@3672 | 2372 | </dd> |
dschulze@5743 | 2373 | <dt id="rotate-function"> |
dschulze@4598 | 2374 | <code class="css">rotate(<angle>)</code> |
vhardy@3672 | 2375 | </dt> |
vhardy@3672 | 2376 | <dd> |
dschulze@4598 | 2377 | specifies a <a href="#RotateDefined">2D rotation</a> by the angle specified in the parameter about the origin of the element, as |
dschulze@6608 | 2378 | defined by the 'transform-origin' property. For example, ''rotate(90deg)'' |
dschulze@4602 | 2379 | would cause elements to appear rotated one-quarter of a turn in the clockwise direction. |
vhardy@3672 | 2380 | </dd> |
dschulze@6608 | 2381 | <dt> |
dschulze@6608 | 2382 | <code class="css">skew(<angle>[, <angle>])</code> |
dschulze@6608 | 2383 | </dt> |
dschulze@6608 | 2384 | <dd> |
dschulze@6608 | 2385 | specifies a <a href="#SkewDefined">2D skew</a> by [ax,ay] for X and Y. If the second parameter is not provided, it has a zero value. |
dschulze@6608 | 2386 | <p class="note">Note that the behavior of ''skew'' is different from mutliplying ''skewX'' with ''skewY''. Implementations must support this function for compatibility with legacy content.</p> |
dschulze@6608 | 2387 | </dd> |
dschulze@5743 | 2388 | <dt id="skewX-function"> |
simon@4365 | 2389 | <code class="css">skewX(<angle>)</code> |
vhardy@3672 | 2390 | </dt> |
vhardy@3672 | 2391 | <dd> |
dschulze@5111 | 2392 | specifies a <a href="#SkewXDefined">2D skew transformation along the X axis</a> by the given angle. |
vhardy@3672 | 2393 | </dd> |
dschulze@5743 | 2394 | <dt id="skewY-function"> |
simon@4365 | 2395 | <code class="css">skewY(<angle>)</code> |
vhardy@3672 | 2396 | </dt> |
vhardy@3672 | 2397 | <dd> |
dschulze@5111 | 2398 | specifies a <a href="#SkewYDefined">2D skew transformation along the Y axis</a> by the given angle. |
vhardy@3672 | 2399 | </dd> |
vhardy@3672 | 2400 | </dl> |
simon@4358 | 2401 | |
simon@4358 | 2402 | |
dschulze@5114 | 2403 | <h3 id="three-d-transform-functions">3D Transform Functions</h3> |
simon@4358 | 2404 | <dl> |
dschulze@5743 | 2405 | <dt id="matrix3d-function"> |
simon@4365 | 2406 | <code class="css">matrix3d(<number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>, <number>)</code> |
simon@4358 | 2407 | </dt> |
simon@4358 | 2408 | <dd> |
simon@4358 | 2409 | specifies a 3D transformation as a 4x4 homogeneous matrix of 16 values in column-major order. |
simon@4358 | 2410 | </dd> |
dschulze@5743 | 2411 | <dt id="translate3d-function"> |
dschulze@4483 | 2412 | <code class="css">translate3d(<translation-value>, <translation-value>, <length>)</code> |
simon@4358 | 2413 | </dt> |
simon@4358 | 2414 | <dd> |
dschulze@4491 | 2415 | 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. |
simon@4358 | 2416 | </dd> |
dschulze@5743 | 2417 | <dt id="translateZ-function"> |
dschulze@4483 | 2418 | <code class="css">translateZ(<length>)</code> |
simon@4358 | 2419 | </dt> |
simon@4358 | 2420 | <dd> |
dschulze@4491 | 2421 | specifies a <a href="#Translate3dDefined">3D translation</a> by the vector [0,0,tz] with the given amount in the Z direction. |
simon@4358 | 2422 | </dd> |
dschulze@5743 | 2423 | <dt id="scale3d-function"> |
simon@4365 | 2424 | <code class="css">scale3d(<number>, <number>, <number>)</code> |
simon@4358 | 2425 | </dt> |
simon@4358 | 2426 | <dd> |
dschulze@4491 | 2427 | specifies a <a href="#Scale3dDefined">3D scale</a> operation by the [sx,sy,sz] scaling vector described by the 3 parameters. |
simon@4358 | 2428 | </dd> |
dschulze@5743 | 2429 | <dt id="scaleZ-function"> |
simon@4365 | 2430 | <code class="css">scaleZ(<number>)</code> |
simon@4358 | 2431 | </dt> |
simon@4358 | 2432 | <dd> |
dschulze@4491 | 2433 | specifies a <a href="#Scale3dDefined">3D scale</a> operation using the [1,1,sz] scaling vector, where sz is given as the parameter. |
simon@4358 | 2434 | </dd> |
dschulze@5743 | 2435 | <dt id="rotate3d-function"> |
simon@4365 | 2436 | <code class="css">rotate3d(<number>, <number>, <number>, <angle>)</code> |
simon@4358 | 2437 | </dt> |
simon@4358 | 2438 | <dd> |
ayg@4745 | 2439 | specifies a <a href="#Rotate3dDefined">3D rotation</a> by the |
ayg@4745 | 2440 | angle specified in last parameter about the [x,y,z] direction |
dschulze@6607 | 2441 | vector described by the first three parameters. A direction vector that cannot be normalized, |
ayg@4745 | 2442 | such as [0,0,0], will cause the rotation to not be applied. |
dschulze@6402 | 2443 | <p class="note">Note that the rotation is clockwise as one looks from the end of |
dschulze@6402 | 2444 | the vector toward the origin.</p> |
dschulze@4491 | 2445 | </dd> |
dschulze@5743 | 2446 | <dt id="rotateX-function"> |
simon@4365 | 2447 | <code class="css">rotateX(<angle>)</code> |
simon@4358 | 2448 | </dt> |
simon@4358 | 2449 | <dd> |
ayg@4745 | 2450 | same as <code class="css">rotate3d(1, 0, 0, <angle>)</code>. |
simon@4358 | 2451 | </dd> |
dschulze@5743 | 2452 | <dt id="rotateY-function"> |
simon@4365 | 2453 | <code class="css">rotateY(<angle>)</code> |
simon@4358 | 2454 | </dt> |
simon@4358 | 2455 | <dd> |
ayg@4745 | 2456 | same as <code class="css">rotate3d(0, 1, 0, <angle>)</code>. |
simon@4358 | 2457 | </dd> |
dschulze@5743 | 2458 | <dt id="rotateZ-function"> |
simon@4365 | 2459 | <code class="css">rotateZ(<angle>)</code> |
simon@4358 | 2460 | </dt> |
simon@4358 | 2461 | <dd> |
ayg@4745 | 2462 | same as <code class="css">rotate3d(0, 0, 1, <angle>)</code>, |
ayg@4745 | 2463 | which is also the same as <code class="css">rotate(<angle>)</code>. |
simon@4358 | 2464 | </dd> |
simon@4577 | 2465 | <dt id="perspective-function"> |
simon@4365 | 2466 | <code class="css">perspective(<length>)</code> |
simon@4358 | 2467 | </dt> |
simon@4358 | 2468 | <dd> |
simon@4700 | 2469 | specifies a <a href="#PerspectiveDefined">perspective projection matrix</a>. This matrix scales points in |
simon@4700 | 2470 | X and Y based on their Z value, scaling points with positive Z values away from the origin, and those with |
simon@4700 | 2471 | negative Z values towards the origin. Points on the z=0 plane are unchanged. The parameter represents the |
simon@4700 | 2472 | distance of the z=0 plane from the viewer. Lower values give a more flattened pyramid and therefore a more |
simon@4700 | 2473 | pronounced perspective effect. For example, a value of 1000px gives a moderate amount of foreshortening |
simon@4700 | 2474 | and a value of 200px gives an extreme amount. The value for depth must be greater than zero, otherwise the |
simon@4700 | 2475 | function is invalid. |
simon@4358 | 2476 | </dd> |
simon@4358 | 2477 | </dl> |
simon@4358 | 2478 | |
simon@4358 | 2479 | <!-- ======================================================================================================= --> |
simon@4358 | 2480 | |
dschulze@5130 | 2481 | <h2 id="transform-function-lists"> |
dschulze@5130 | 2482 | The Transform Function Lists |
vhardy@3672 | 2483 | </h2> |
dschulze@5910 | 2484 | |
vhardy@3672 | 2485 | <p> |
dschulze@5130 | 2486 | If a list of <var><transform-functions></var> is provided, then the net effect is as if each transform function had been specified separately in the order provided. For example, |
vhardy@3672 | 2487 | </p> |
dschulze@5910 | 2488 | |
simon@4358 | 2489 | <pre> |
simon@4358 | 2490 | <div style="transform:translate(-10px,-20px) scale(2) rotate(45deg) translate(5px,10px)"/> |
simon@4358 | 2491 | </pre> |
dschulze@5910 | 2492 | |
vhardy@3672 | 2493 | <p> |
vhardy@3672 | 2494 | is functionally equivalent to: |
vhardy@3672 | 2495 | </p> |
simon@4358 | 2496 | <pre> |
simon@4358 | 2497 | <div style="transform:translate(-10px,-20px)"> |
simon@4358 | 2498 | <div style="transform:scale(2)"> |
simon@4358 | 2499 | <div style="transform:rotate(45deg)"> |
simon@4358 | 2500 | <div style="transform:translate(5px,10px)"> |
simon@4358 | 2501 | </div> |
simon@4358 | 2502 | </div> |
simon@4358 | 2503 | </div> |
simon@4358 | 2504 | </div> |
simon@4358 | 2505 | </pre> |
dschulze@5910 | 2506 | <p> |
dschulze@5910 | 2507 | That is, in the absence of other styling that affects position and dimensions, a |
dschulze@5910 | 2508 | nested set of transforms is equivalent to a single list of transform functions, |
dschulze@5910 | 2509 | applied from the outside in. The resulting transform is the matrix multiplication of |
dschulze@5910 | 2510 | the list of transforms. |
dschulze@5910 | 2511 | </p> |
dschulze@5910 | 2512 | |
dschulze@5910 | 2513 | <p> |
dschulze@5931 | 2514 | If a transform function causes the <span class="term">current transformation matrix |
ayg@5989 | 2515 | (CTM)</span> of an object to be non-invertible, the object and its content do not |
dschulze@5931 | 2516 | get displayed. |
dschulze@5910 | 2517 | </p> |
dschulze@5910 | 2518 | |
dschulze@5910 | 2519 | <div class="example"> |
dschulze@5910 | 2520 | <p> |
dschulze@5910 | 2521 | The object in the following example gets scaled by 0. |
dschulze@5910 | 2522 | </p> |
dschulze@5910 | 2523 | |
dschulze@5910 | 2524 | <pre><style> |
dschulze@5910 | 2525 | .box { |
dschulze@5910 | 2526 | transform: scale(0); |
dschulze@5910 | 2527 | } |
dschulze@5910 | 2528 | </style> |
dschulze@5910 | 2529 | |
dschulze@5910 | 2530 | <div class="box"> |
dschulze@5910 | 2531 | Not visible |
dschulze@5910 | 2532 | </div></pre> |
dschulze@5910 | 2533 | |
dschulze@5910 | 2534 | <p> |
dschulze@5910 | 2535 | The scaling causes a non-invertible CTM for the coordinate space of the div box. |
dschulze@5910 | 2536 | Therefore neither the div box, nor the text in it get displayed. |
dschulze@5910 | 2537 | </p> |
dschulze@5910 | 2538 | </div> |
dschulze@5690 | 2539 | |
dschulze@5690 | 2540 | <!-- ======================================================================================================= --> |
vhardy@3672 | 2541 | |
vhardy@3672 | 2542 | <h2 id="animation"> |
dschulze@5690 | 2543 | Interpolation of Transforms |
vhardy@3672 | 2544 | </h2> |
vhardy@3672 | 2545 | |
vhardy@3672 | 2546 | <p> |
dschulze@5690 | 2547 | When animating or transitioning transforms, the transform function lists must be |
dschulze@5690 | 2548 | interpolated. For interpolation between one transform <em>from-transform</em> and |
dschulze@5690 | 2549 | a second transforms <em>to-transform</em>, the rules described below are applied. |
vhardy@3672 | 2550 | </p> |
vhardy@3672 | 2551 | |
vhardy@3672 | 2552 | <ul> |
dschulze@5743 | 2553 | <li id="none-none-animation"> |
dschulze@5743 | 2554 | If both the <em>from-</em> and <em>to-transform</em> are ''none'': |
vhardy@3672 | 2555 | <ul> |
vhardy@3672 | 2556 | <li> |
dschulze@5743 | 2557 | There is no interpolation necessary. The computed value stays ''none''. |
vhardy@3672 | 2558 | </li> |
vhardy@3672 | 2559 | </ul> |
vhardy@3672 | 2560 | </li> |
dschulze@5743 | 2561 | <li id="none-transform-animation"> |
dschulze@5743 | 2562 | If one of the <em>from-</em> or <em>to-transforms</em> is ''none''. |
vhardy@3672 | 2563 | <ul> |
vhardy@3672 | 2564 | <li> |
dschulze@5743 | 2565 | The value ''none'' is replaced by an equivalent |
dschulze@5690 | 2566 | <a href="#TermIdentityTransformFunction"><i>identity transform |
dschulze@5690 | 2567 | function</i></a> list for the corresponding transform |
dschulze@5690 | 2568 | function list. Both transform function lists get interpolated following the |
dschulze@5690 | 2569 | next rule. |
vhardy@3672 | 2570 | </li> |
vhardy@3672 | 2571 | </ul> |
dschulze@5690 | 2572 | <div class="example"> |
dschulze@5690 | 2573 | <p> |
dschulze@5743 | 2574 | For example, if <em>from-transform</em> is ''scale(2)'' and |
dschulze@5743 | 2575 | <em>to-transform</em> is ''none'' then the value ''scale(1)'' will be used for |
dschulze@5743 | 2576 | <em>to-transform</em> and animation will proceed using the next rule. |
dschulze@5892 | 2577 | Similarly, if <em>from-transform</em> is ''none'' and <em>to-transform</em> is |
dschulze@5743 | 2578 | ''scale(2) rotate(50deg)'' then the animation will execute as if |
dschulze@5743 | 2579 | <em>from-transform</em> is ''scale(1) rotate(0)''. |
dschulze@5690 | 2580 | </p> |
dschulze@5690 | 2581 | </div> |
vhardy@3672 | 2582 | </li> |
dschulze@5743 | 2583 | <li id="transform-transform-animation"> |
dschulze@5690 | 2584 | If <em>from-</em> and <em>to-transform</em> have the same number of |
dschulze@6402 | 2585 | transform functions, each transform function pair has either the same name, or is |
dschulze@6402 | 2586 | a derivative of the same <a href="#transform-primitives">primitive</a>. |
vhardy@3672 | 2587 | <ul> |
vhardy@3672 | 2588 | <li> |
dschulze@5690 | 2589 | Interpolate each transform function pair as described in <a |
dschulze@5690 | 2590 | href="#interpolation-of-transform-functions">Interpolation of transform |
dschulze@5690 | 2591 | functions</a>. The computed value is the resulting transform function |
dschulze@5690 | 2592 | list. |
vhardy@3672 | 2593 | </li> |
vhardy@3672 | 2594 | </ul> |
dschulze@5690 | 2595 | <div class="example"> |
dschulze@5690 | 2596 | <p> |
dschulze@5743 | 2597 | For example, if <em>from-transform</em> is ''scale(1) translate(0)'' and |
dschulze@5743 | 2598 | <em>to-transform</em> is ''translate(100px) scale(2)'' then ''scale(1)'' and |
dschulze@5743 | 2599 | ''translate(100px)'' as well as ''translate(0)'' and ''scale(2)'' don't share |
dschulze@5743 | 2600 | a common primitive and therefore can not get interpolated following this rule. |
dschulze@5690 | 2601 | </p> |
dschulze@5690 | 2602 | </div> |
vhardy@3672 | 2603 | </li> |
dschulze@5743 | 2604 | <li id="other-animation"> |
dschulze@5690 | 2605 | In all other cases: |
vhardy@3672 | 2606 | <ul> |
vhardy@3672 | 2607 | <li> |
dschulze@5690 | 2608 | The transform functions of each transform function list on the |
dschulze@5892 | 2609 | <em>from-</em> and <em>to-transform</em> get post multiplied and |
dschulze@5892 | 2610 | converted into 4x4 matrices. Each of the matrices gets interpolated following |
dschulze@5892 | 2611 | the instructions in <a href="#matrix-interpolation">Interpolation of |
dschulze@5892 | 2612 | matrices</a>. The computed value is the transform function ''matrix'' if both |
dschulze@5743 | 2613 | initial matrices can be represented by a correlating 3x2 matrix and |
dschulze@5743 | 2614 | ''matrix3d'' otherwise. |
vhardy@3672 | 2615 | </li> |
vhardy@3672 | 2616 | </ul> |
vhardy@3672 | 2617 | </li> |
vhardy@3672 | 2618 | </ul> |
vhardy@3672 | 2619 | |
vhardy@3672 | 2620 | <p> |
dschulze@5690 | 2621 | In some cases, an animation might cause a transformation matrix to be singular or |
dschulze@5690 | 2622 | non-invertible. For example, an animation in which scale moves from 1 to -1. At the |
dschulze@5690 | 2623 | time when the matrix is in such a state, the transformed element is not rendered. |
vhardy@3672 | 2624 | </p> |
dschulze@5892 | 2625 | |
dschulze@5892 | 2626 | <!-- ======================================================================================================= --> |
vhardy@3672 | 2627 | |
dschulze@5892 | 2628 | <h2 id="transform-primitives"> |
dschulze@5690 | 2629 | Transform function primitives and derivatives |
dschulze@5892 | 2630 | </h2> |
dschulze@5690 | 2631 | |
dschulze@5690 | 2632 | <p> |
dschulze@5690 | 2633 | Some transform functions can be represented by more generic transform functions. |
dschulze@5690 | 2634 | These transform functions are called derived transform functions, the generic |
dschulze@5690 | 2635 | transform functions primitives. Primitives for two-dimensional and |
dschulze@5690 | 2636 | three-dimensional transform functions are listed below. |
dschulze@5690 | 2637 | </p> |
dschulze@5690 | 2638 | |
dschulze@5690 | 2639 | <p> |
dschulze@6299 | 2640 | Two-dimensional primitives with derived transform functions are: |
dschulze@5690 | 2641 | </p> |
dschulze@5690 | 2642 | |
dschulze@5690 | 2643 | <dl> |
dschulze@5743 | 2644 | <dt id="translate-primitive"> |
dschulze@5690 | 2645 | <code class="css">translate(<translation-value>, |
dschulze@5690 | 2646 | <translation-value>)</code> |
dschulze@5690 | 2647 | </dt> |
dschulze@5690 | 2648 | <dd> |
dschulze@5690 | 2649 | for <code class="css">translateX(<translation-value>)</code>, |
dschulze@5690 | 2650 | <code class="css">translateY(<translation-value>)</code> and |
dschulze@5690 | 2651 | <code class="css">translate(<translation-value>)</code>. |
dschulze@5690 | 2652 | </dd> |
dschulze@5743 | 2653 | <dt id="rotate-three-primitive"> |
dschulze@5690 | 2654 | <code class="css">rotate(<angle>, <translation-value>, |
dschulze@5690 | 2655 | <translation-value>)</code> |
dschulze@5690 | 2656 | </dt> |
dschulze@5690 | 2657 | <dd> |
dschulze@5690 | 2658 | for <code class="css">rotate(<angle>)</code> if |
dschulze@5690 | 2659 | <a href="#svg-transform-functions">rotate with three arguments</a> is supported. |
dschulze@5690 | 2660 | </dd> |
dschulze@5743 | 2661 | <dt id="scale-primitive"> |
dschulze@5690 | 2662 | <code class="css">scale(<number>, <number>)</code> |
dschulze@5690 | 2663 | </dt> |
dschulze@5690 | 2664 | <dd> |
dschulze@5690 | 2665 | for <code class="css">scaleX(<number>)</code>, <code |
dschulze@5690 | 2666 | class="css">scaleY(<number>)</code> and <code |
dschulze@5690 | 2667 | class="css">scale(<number>)</code>. |
dschulze@5690 | 2668 | </dd> |
dschulze@5690 | 2669 | </dl> |
dschulze@5690 | 2670 | |
dschulze@5690 | 2671 | <p> |
dschulze@6299 | 2672 | Three-dimensional primitives with derived transform functions are: |
dschulze@5690 | 2673 | </p> |
dschulze@5690 | 2674 | |
dschulze@5690 | 2675 | <dl> |
dschulze@5743 | 2676 | <dt id="translate3d-primitive"> |
dschulze@5690 | 2677 | <code class="css">translate3d(<translation-value>, |
dschulze@5690 | 2678 | <translation-value>, <length>)</code> |
dschulze@5690 | 2679 | </dt> |
dschulze@5690 | 2680 | <dd> |
dschulze@5690 | 2681 | for <code class="css">translateX(<translation-value>)</code>, <code |
dschulze@5690 | 2682 | class="css">translateY(<translation-value>)</code>, <code |
dschulze@5690 | 2683 | class="css">translateZ(<number>)</code> and <code |
dschulze@5690 | 2684 | class="css">translate(<translation-value>[, |
dschulze@5690 | 2685 | <translation-value>])</code>. |
dschulze@5690 | 2686 | </dd> |
dschulze@5743 | 2687 | <dt id="scale3d-primitive"> |
dschulze@5690 | 2688 | <code class="css">scale3d(<number>, <number>, <number>)</code> |
dschulze@5690 | 2689 | </dt> |
dschulze@5690 | 2690 | <dd> |
dschulze@5690 | 2691 | for <code class="css">scaleX(<number>)</code>, <code |
dschulze@5690 | 2692 | class="css">scaleY(<number>)</code>, <code |
dschulze@5690 | 2693 | class="css">scaleZ(<number>)</code> and <code |
dschulze@5690 | 2694 | class="css">scale(<number>[, <number>])</code>. |
dschulze@5690 | 2695 | </dd> |
dschulze@6402 | 2696 | <dt id="rotate3d-primitive"> |
bert@6704 | 2697 | <code class="css">rotate3d(<number>, <number>, <number>, <angle>)</code> |
dschulze@6402 | 2698 | </dt> |
dschulze@6402 | 2699 | <dd> |
dschulze@6402 | 2700 | for <code class="css">rotate(<number>)</code>, <code |
dschulze@6402 | 2701 | class="css">rotateX(<number>)</code>, <code |
dschulze@6402 | 2702 | class="css">rotateY(<number>)</code> and <code |
dschulze@6402 | 2703 | class="css">rotateZ(<number>)</code>. |
dschulze@6402 | 2704 | </dd> |
dschulze@5690 | 2705 | </dl> |
dschulze@5690 | 2706 | |
dschulze@5743 | 2707 | <p id="interpolation-two-three-dimensional-function"> |
dschulze@5690 | 2708 | For derived transform functions that have a two-dimensional primitive and a |
dschulze@5690 | 2709 | three-dimensional primitive, the context decides about the used primitive. See |
dschulze@5690 | 2710 | <a href="#interpolation-of-transform-functions">Interpolation of primitives and |
dschulze@5690 | 2711 | derived transform functions</a>. |
dschulze@5690 | 2712 | </p> |
dschulze@5892 | 2713 | |
dschulze@5892 | 2714 | <!-- ======================================================================================================= --> |
dschulze@5690 | 2715 | |
dschulze@5892 | 2716 | <h2 id="interpolation-of-transform-functions"> |
dschulze@5690 | 2717 | Interpolation of primitives and derived transform functions |
dschulze@5892 | 2718 | </h2> |
dschulze@5690 | 2719 | |
dschulze@5690 | 2720 | <p> |
dschulze@6402 | 2721 | Two transform functions with the same name and the same number of arguments are |
dschulze@5690 | 2722 | interpolated numerically without a former conversion. The calculated value will be |
dschulze@6402 | 2723 | of the same transform function type with the same number of arguments. Special rules |
dschulze@6402 | 2724 | apply to ''rotate3d'', ''matrix'', ''matrix3d'' and ''perspective''. |
dschulze@5690 | 2725 | </p> |
dschulze@5690 | 2726 | |
dschulze@5690 | 2727 | <div class="example"> |
dschulze@5690 | 2728 | <p> |
dschulze@5743 | 2729 | The two transform functions ''translate(0)'' and ''translate(100px)'' are of the |
dschulze@5743 | 2730 | same type, have the same number of arguments and therefore can get interpolated |
dschulze@5743 | 2731 | numerically. ''translateX(100px)'' is not of the same type and |
dschulze@5743 | 2732 | ''translate(100px, 0)'' does not have the same number of arguments, therefore |
dschulze@5743 | 2733 | these transform functions can not get interpolated without a former conversion |
dschulze@5743 | 2734 | step. |
dschulze@5690 | 2735 | </p> |
dschulze@5690 | 2736 | </div> |
dschulze@5690 | 2737 | |
dschulze@5690 | 2738 | <p> |
dschulze@5690 | 2739 | Two different types of transform functions that share the same primitive, or |
dschulze@5690 | 2740 | transform functions of the same type with different number of arguments can be |
dschulze@5690 | 2741 | interpolated. Both transform functions need a former conversion to the common |
dschulze@5690 | 2742 | primitive first and get interpolated numerically afterwards. The computed value |
dschulze@5690 | 2743 | will be the primitive with the resulting interpolated arguments. |
dschulze@5690 | 2744 | </p> |
dschulze@5690 | 2745 | |
dschulze@5690 | 2746 | <div class="example"> |
dschulze@5690 | 2747 | <p> |
dschulze@5743 | 2748 | The following example describes a transition from ''translateX(100px)'' to |
dschulze@5743 | 2749 | ''translateY(100px)'' in 3 seconds on hovering over the div box. Both transform |
dschulze@5743 | 2750 | functions derive from the same primitive <code |
dschulze@5690 | 2751 | class="css">translate(<translation-value>, <translation-value>)</code> |
dschulze@5690 | 2752 | and therefore can be interpolated. |
dschulze@5690 | 2753 | </p> |
dschulze@5690 | 2754 | |
dschulze@5690 | 2755 | <pre>div { |
dschulze@5690 | 2756 | transform: translateX(100px); |
dschulze@5690 | 2757 | } |
dschulze@5690 | 2758 | |
dschulze@5690 | 2759 | div:hover { |
dschulze@5690 | 2760 | transform: translateY(100px); |
dschulze@5690 | 2761 | transition: transform 3s; |
dschulze@5690 | 2762 | }</pre> |
dschulze@5690 | 2763 | |
dschulze@5690 | 2764 | <p> |
dschulze@5690 | 2765 | For the time of the transition both transform functions get transformed to the |
dschulze@5743 | 2766 | common primitive. ''translateX(100px)'' gets converted to ''translate(100px, 0)'' |
dschulze@5743 | 2767 | and ''translateY(100px)'' gets converted to ''translate(0, 100px)''. Both |
dschulze@5743 | 2768 | transform functions can then get interpolated numerically. |
dschulze@5690 | 2769 | </p> |
dschulze@5690 | 2770 | </div> |
dschulze@5690 | 2771 | |
dschulze@5690 | 2772 | <p> |
dschulze@5690 | 2773 | If both transform functions share a primitive in the two-dimensional space, both |
dschulze@5690 | 2774 | transform functions get converted to the two-dimensional primitive. If one or both |
dschulze@5690 | 2775 | transform functions are three-dimensional transform functions, the common |
dschulze@5690 | 2776 | three-dimensional primitive is used. |
dschulze@5690 | 2777 | </p> |
dschulze@5690 | 2778 | |
dschulze@5690 | 2779 | <div class="example"> |
dschulze@5690 | 2780 | <p> |
dschulze@5690 | 2781 | In this example a two-dimensional transform function gets animated to a |
dschulze@5690 | 2782 | three-dimensional transform function. The common primitive is <code |
dschulze@5690 | 2783 | class="css">translate3d</code>. |
dschulze@5690 | 2784 | </p> |
dschulze@5690 | 2785 | |
dschulze@5690 | 2786 | <pre>div { |
dschulze@5690 | 2787 | transform: translateX(100px); |
dschulze@5690 | 2788 | } |
dschulze@5690 | 2789 | |
dschulze@5690 | 2790 | div:hover { |
dschulze@5690 | 2791 | transform: translateZ(100px); |
dschulze@5690 | 2792 | transition: transform 3s; |
dschulze@5690 | 2793 | }</pre> |
dschulze@5690 | 2794 | |
dschulze@5690 | 2795 | <p> |
dschulze@5690 | 2796 | First '<code class="css">translateX(100px)</code>' gets converted to '<code |
dschulze@5690 | 2797 | class="css">translate3d(100px, 0, 0)</code>' and '<code class="css">translateZ(100px)</code>' to '<code |
dschulze@5690 | 2798 | class="css">translate3d(0, 0, 100px)</code>' respectively. Then both converted transform |
dschulze@5690 | 2799 | functions get interpolated numerically. |
dschulze@5690 | 2800 | </p> |
dschulze@5690 | 2801 | </div> |
dschulze@5690 | 2802 | |
dschulze@6299 | 2803 | <p> |
dschulze@6607 | 2804 | The transform functions ''matrix'', ''matrix3d'' and ''perspective'' |
dschulze@6299 | 2805 | get converted into 4x4 matrices first and interpolated as defined in section <a |
dschulze@6402 | 2806 | href="#matrix-interpolation">Interpolation of Matrices</a> afterwards. |
dschulze@6299 | 2807 | </p> |
dschulze@6299 | 2808 | |
dschulze@6607 | 2809 | <p> |
dschulze@6607 | 2810 | For interpolatations with the primitive ''rotate3d'', the direction vectors of the |
dschulze@6607 | 2811 | transform functions get normalized first. If the |
dschulze@6607 | 2812 | normalized vectors are equal, the rotation angle gets interpolated numerically. |
dschulze@6607 | 2813 | Otherwise the transform functions get converted into 4x4 matrices first and |
dschulze@6607 | 2814 | interpolated as defined in section <a href="#matrix-interpolation">Interpolation of |
dschulze@6607 | 2815 | Matrices</a> afterwards. |
dschulze@6607 | 2816 | </p> |
dschulze@6468 | 2817 | |
dschulze@5690 | 2818 | <!-- ======================================================================================================= --> |
dschulze@5690 | 2819 | |
dschulze@5892 | 2820 | <h2 id="matrix-interpolation"> |
dschulze@5892 | 2821 | Interpolation of Matrices |
vhardy@3672 | 2822 | </h2> |
vhardy@3672 | 2823 | |
vhardy@3672 | 2824 | <p> |
dschulze@5892 | 2825 | When interpolating between two matrices, each is decomposed into the corresponding |
dschulze@5892 | 2826 | translation, rotation, scale, skew and perspective values. Not all matrices can be |
dschulze@5892 | 2827 | accurately described by these values. Those that can't are decomposed into the most |
dschulze@5892 | 2828 | accurate representation possible, using the pseudocode in <a |
bert@6704 | 2829 | href="#matrix-decomposing">Decomposing the Matrix</a>. The resulting values get <a |
bert@6704 | 2830 | href="#matrix-values-interpolation">interpolated numerically</a> and <a |
bert@6704 | 2831 | href="#matrix-recomposing">recomposed back to a matrix</a> in a final step. |
vhardy@3672 | 2832 | </p> |
vhardy@3672 | 2833 | |
dschulze@5902 | 2834 | <div class="note"> |
dschulze@5902 | 2835 | <p> |
dschulze@5902 | 2836 | In the following example the element gets translated by 100 pixel in both the X |
dschulze@5902 | 2837 | and Y directions and rotated by 1170 degree on hovering. The initial |
dschulze@5902 | 2838 | transformation is 45 degree. With the usage of transition, an author might expect |
dschulze@5902 | 2839 | a animated, clockwise rotation by three and a quarter turn (1170 degree). |
dschulze@5902 | 2840 | </p> |
dschulze@5902 | 2841 | |
dschulze@5902 | 2842 | <pre> |
dschulze@5902 | 2843 | <style> |
dschulze@5902 | 2844 | div { |
dschulze@5902 | 2845 | transform: rotate(45deg); |
dschulze@5902 | 2846 | } |
dschulze@5902 | 2847 | div:hover { |
dschulze@5902 | 2848 | transform: translate(100px, 100px) rotate(1215deg); |
dschulze@5902 | 2849 | transition: transform 3s; |
dschulze@5902 | 2850 | } |
dschulze@5902 | 2851 | </style> |
dschulze@5902 | 2852 | |
dschulze@5902 | 2853 | <div></div></pre> |
dschulze@5902 | 2854 | |
dschulze@5902 | 2855 | <p> |
dschulze@5902 | 2856 | The number of transform functions on the source transform |
dschulze@5902 | 2857 | ''rotate(45deg)'' differs from the number of transform functions on the |
dschulze@5902 | 2858 | destination transform ''translate(100px, 100px) rotate(1125deg)''. |
dschulze@5902 | 2859 | According to the last rule of <a href="#animation">Interpolation of |
dschulze@5902 | 2860 | Transforms</a>, both transforms must be interpolated by matrix interpolation. |
dschulze@5902 | 2861 | With converting the transformation functions to matrices, the information about |
dschulze@5902 | 2862 | the three turns gets lost and the element gets rotated by just a quarter turn (90 |
dschulze@5902 | 2863 | degree). |
dschulze@5902 | 2864 | </p> |
dschulze@5902 | 2865 | |
dschulze@5902 | 2866 | <p> |
dschulze@5902 | 2867 | To achieve the three and a quarter turns for the example above, source and |
dschulze@5902 | 2868 | destination transforms must fulfill the third rule of <a |
dschulze@5902 | 2869 | href="#animation">Interpolation of Transforms</a>. Source transform could look |
dschulze@5902 | 2870 | like ''translate(0, 0) rotate(45deg)'' for a linearly interpolation of |
dschulze@5902 | 2871 | the transform functions. |
dschulze@5902 | 2872 | </p> |
dschulze@5902 | 2873 | </div> |
dschulze@5903 | 2874 | |
dschulze@5903 | 2875 | <p> |
dschulze@5903 | 2876 | If one of the matrices for interpolation is non-invertible, the used animation |
dschulze@5903 | 2877 | function must fallback to a discrete animation according to the rules of the |
dschulze@5903 | 2878 | respective animation specification. |
dschulze@5903 | 2879 | </p> |
dschulze@5902 | 2880 | |
dschulze@5892 | 2881 | <h3 id="matrix-decomposing">Decomposing the Matrix</h3> |
dschulze@5892 | 2882 | |
dschulze@5892 | 2883 | <p> |
dschulze@5892 | 2884 | The pseudocode below is based upon the "unmatrix" method in "Graphics Gems II, |
dschulze@5892 | 2885 | edited by Jim Arvo", but modified to use Quaternions instead of Euler angles to |
dschulze@5892 | 2886 | avoid the problem of Gimbal Locks. |
dschulze@5892 | 2887 | </p> |
dschulze@5892 | 2888 | |
dschulze@5892 | 2889 | <p> |
dschulze@5892 | 2890 | The following pseudocode works on a 4x4 homogeneous matrix: |
dschulze@5892 | 2891 | </p> |
dschulze@5892 | 2892 | |
vhardy@3672 | 2893 | <pre> |
dschulze@5892 | 2894 | Input: matrix ; a 4x4 matrix |
dschulze@5892 | 2895 | Output: translation ; a 3 component vector |
dschulze@5892 | 2896 | scale ; a 3 component vector |
dschulze@5892 | 2897 | skew ; skew factors XY,XZ,YZ represented as a 3 component vector |
dschulze@5892 | 2898 | perspective ; a 4 component vector |
dschulze@5892 | 2899 | quaternion ; a 4 component vector |
dschulze@5892 | 2900 | Returns false if the matrix cannot be decomposed, true if it can |
vhardy@3672 | 2901 | |
dschulze@5892 | 2902 | Supporting functions (point is a 3 component vector, matrix is a 4x4 matrix): |
dschulze@5892 | 2903 | double determinant(matrix) returns the 4x4 determinant of the matrix |
dschulze@5892 | 2904 | matrix inverse(matrix) returns the inverse of the passed matrix |
dschulze@5892 | 2905 | matrix transpose(matrix) returns the transpose of the passed matrix |
dschulze@5892 | 2906 | point multVecMatrix(point, matrix) multiplies the passed point by the passed matrix |
dschulze@5892 | 2907 | and returns the transformed point |
dschulze@5892 | 2908 | double length(point) returns the length of the passed vector |
dschulze@5892 | 2909 | point normalize(point) normalizes the length of the passed point to 1 |
dschulze@5892 | 2910 | double dot(point, point) returns the dot product of the passed points |
dschulze@5892 | 2911 | double sqrt(double) returns the root square of passed value |
dschulze@5892 | 2912 | double max(double y, double x) returns the bigger value of the two passed values |
vhardy@3672 | 2913 | |
dschulze@5892 | 2914 | Decomposition also makes use of the following function: |
dschulze@5892 | 2915 | point combine(point a, point b, double ascl, double bscl) |
dschulze@5892 | 2916 | result[0] = (ascl * a[0]) + (bscl * b[0]) |
dschulze@5892 | 2917 | result[1] = (ascl * a[1]) + (bscl * b[1]) |
dschulze@5892 | 2918 | result[2] = (ascl * a[2]) + (bscl * b[2]) |
dschulze@5892 | 2919 | return result |
vhardy@3672 | 2920 | |
dschulze@5892 | 2921 | // Normalize the matrix. |
dschulze@5892 | 2922 | if (matrix[3][3] == 0) |
dschulze@5892 | 2923 | return false |
vhardy@3672 | 2924 | |
dschulze@5892 | 2925 | for (i = 0; i < 4; i++) |
dschulze@5892 | 2926 | for (j = 0; j < 4; j++) |
dschulze@5892 | 2927 | matrix[i][j] /= matrix[3][3] |
dschulze@4484 | 2928 | |
dschulze@5892 | 2929 | // perspectiveMatrix is used to solve for perspective, but it also provides |
dschulze@5892 | 2930 | // an easy way to test for singularity of the upper 3x3 component. |
dschulze@5892 | 2931 | perspectiveMatrix = matrix |
dschulze@4484 | 2932 | |
dschulze@5892 | 2933 | for (i = 0; i < 3; i++) |
dschulze@5892 | 2934 | perspectiveMatrix[i][3] = 0 |
dschulze@4484 | 2935 | |
dschulze@5892 | 2936 | perspectiveMatrix[3][3] = 1 |
dschulze@4484 | 2937 | |
dschulze@5892 | 2938 | if (determinant(perspectiveMatrix) == 0) |
dschulze@5892 | 2939 | return false |
dschulze@4484 | 2940 | |
dschulze@5892 | 2941 | // First, isolate perspective. |
dschulze@5892 | 2942 | if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0) |
dschulze@5892 | 2943 | // rightHandSide is the right hand side of the equation. |
dschulze@5892 | 2944 | rightHandSide[0] = matrix[0][3]; |
dschulze@5892 | 2945 | rightHandSide[1] = matrix[1][3]; |
dschulze@5892 | 2946 | rightHandSide[2] = matrix[2][3]; |
dschulze@5892 | 2947 | rightHandSide[3] = matrix[3][3]; |
dschulze@4484 | 2948 | |
dschulze@5892 | 2949 | // Solve the equation by inverting perspectiveMatrix and multiplying |
dschulze@5892 | 2950 | // rightHandSide by the inverse. |
dschulze@5892 | 2951 | inversePerspectiveMatrix = inverse(perspectiveMatrix) |
dschulze@5892 | 2952 | transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix) |
dschulze@5892 | 2953 | perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix) |
dschulze@5892 | 2954 | else |
dschulze@5892 | 2955 | // No perspective. |
dschulze@5892 | 2956 | perspective[0] = perspective[1] = perspective[2] = 0 |
dschulze@5892 | 2957 | perspective[3] = 1 |
dschulze@4484 | 2958 | |
dschulze@5892 | 2959 | // Next take care of translation |
dschulze@5892 | 2960 | for (i = 0; i < 3; i++) |
dschulze@5892 | 2961 | translate[i] = matrix[3][i] |
dschulze@4484 | 2962 | |
dschulze@5892 | 2963 | // Now get scale and shear. 'row' is a 3 element array of 3 component vectors |
dschulze@5892 | 2964 | for (i = 0; i < 3; i++) |
dschulze@5892 | 2965 | row[i][0] = matrix[i][0] |
dschulze@5892 | 2966 | row[i][1] = matrix[i][1] |
dschulze@5892 | 2967 | row[i][2] = matrix[i][2] |
vhardy@3672 | 2968 | |
dschulze@5892 | 2969 | // Compute X scale factor and normalize first row. |
dschulze@5892 | 2970 | scale[0] = length(row[0]) |
dschulze@5892 | 2971 | row[0] = normalize(row[0]) |
vhardy@3672 | 2972 | |
dschulze@5892 | 2973 | // Compute XY shear factor and make 2nd row orthogonal to 1st. |
dschulze@5892 | 2974 | skew[0] = dot(row[0], row[1]) |
dschulze@5892 | 2975 | row[1] = combine(row[1], row[0], 1.0, -skew[0]) |
vhardy@3672 | 2976 | |
dschulze@5892 | 2977 | // Now, compute Y scale and normalize 2nd row. |
dschulze@5892 | 2978 | scale[1] = length(row[1]) |
dschulze@5892 | 2979 | row[1] = normalize(row[1]) |
dschulze@5892 | 2980 | skew[0] /= scale[1]; |
vhardy@3672 | 2981 | |
dschulze@5892 | 2982 | // Compute XZ and YZ shears, orthogonalize 3rd row |
dschulze@5892 | 2983 | skew[1] = dot(row[0], row[2]) |
dschulze@5892 | 2984 | row[2] = combine(row[2], row[0], 1.0, -skew[1]) |
dschulze@5892 | 2985 | skew[2] = dot(row[1], row[2]) |
dschulze@5892 | 2986 | row[2] = combine(row[2], row[1], 1.0, -skew[2]) |
vhardy@3672 | 2987 | |
dschulze@5892 | 2988 | // Next, get Z scale and normalize 3rd row. |
dschulze@5892 | 2989 | scale[2] = length(row[2]) |
dschulze@5892 | 2990 | row[2] = normalize(row[2]) |
dschulze@5892 | 2991 | skew[1] /= scale[2] |
dschulze@5892 | 2992 | skew[2] /= scale[2] |
dschulze@4484 | 2993 | |
dschulze@5892 | 2994 | // At this point, the matrix (in rows) is orthonormal. |
dschulze@5892 | 2995 | // Check for a coordinate system flip. If the determinant |
dschulze@5892 | 2996 | // is -1, then negate the matrix and the scaling factors. |
dschulze@5892 | 2997 | pdum3 = cross(row[1], row[2]) |
dschulze@5892 | 2998 | if (dot(row[0], pdum3) < 0) |
dschulze@5892 | 2999 | for (i = 0; i < 3; i++) |
dschulze@6880 | 3000 | scale[i] *= -1; |
dschulze@5892 | 3001 | row[i][0] *= -1 |
dschulze@5892 | 3002 | row[i][1] *= -1 |
dschulze@5892 | 3003 | row[i][2] *= -1 |
dschulze@4484 | 3004 | |
dschulze@5892 | 3005 | // Now, get the rotations out |
dschulze@5892 | 3006 | quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][1] - row[2][2], 0)) |
dschulze@5892 | 3007 | quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][1] - row[2][2], 0)) |
dschulze@5892 | 3008 | quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][1] + row[2][2], 0)) |
dschulze@5892 | 3009 | quaternion[3] = 0.5 * sqrt(max(1 + row[0][0] + row[1][1] + row[2][2], 0)) |
dschulze@4484 | 3010 | |
dschulze@5892 | 3011 | if (row[2][1] > row[1][2]) |
dschulze@5892 | 3012 | quaternion[0] = -quaternion[0] |
dschulze@5892 | 3013 | if (row[0][2] > row[2][0]) |
dschulze@5892 | 3014 | quaternion[1] = -quaternion[1] |
dschulze@5892 | 3015 | if (row[1][0] > row[0][1]) |
dschulze@5892 | 3016 | quaternion[2] = -quaternion[2] |
vhardy@3672 | 3017 | |
dschulze@5892 | 3018 | return true</pre> |
vhardy@3672 | 3019 | |
dschulze@5892 | 3020 | <h3 id="matrix-values-interpolation"> |
dschulze@5892 | 3021 | Interpolation of decomposed matrix values |
dschulze@5892 | 3022 | </h3> |
dschulze@5892 | 3023 | |
vhardy@3672 | 3024 | <p> |
dschulze@5892 | 3025 | Each component of the decomposed values translation, scale, skew and perspective of |
dschulze@5892 | 3026 | the source matrix get linearly interpolated with each corresponding component of the |
dschulze@5892 | 3027 | destination matrix. |
vhardy@3672 | 3028 | </p> |
dschulze@5892 | 3029 | |
dschulze@5892 | 3030 | <p class="note"> |
dschulze@5892 | 3031 | For instance, <code>translate[0]</code> of the source matrix and |
dschulze@5892 | 3032 | <code>translate[0]</code> of the destination matrix are interpolated numerically, |
dschulze@5892 | 3033 | and the result is used to set the translation of the animating element. |
dschulze@5892 | 3034 | </p> |
dschulze@5892 | 3035 | |
vhardy@3672 | 3036 | <p> |
dschulze@5892 | 3037 | Quaternions of the decomposed source matrix are interpolated with quaternions of the |
dschulze@5892 | 3038 | decomposed destination matrix using the spherical linear interpolation (Slerp) as |
dschulze@5892 | 3039 | described by the pseudocode below: |
vhardy@3672 | 3040 | </p> |
dschulze@5892 | 3041 | |
vhardy@3672 | 3042 | <pre> |
dschulze@5892 | 3043 | Input: quaternionA ; a 4 component vector |
dschulze@5892 | 3044 | quaternionB ; a 4 component vector |
dschulze@5892 | 3045 | t ; interpolation parameter with 0 <= t <= 1 |
dschulze@5892 | 3046 | Output: quaternionDst ; a 4 component vector |
dschulze@5892 | 3047 | |
dschulze@5892 | 3048 | Supporting functions (vector is a 4 component vector): |
dschulze@5892 | 3049 | double dot(vector, vector) returns the dot product of the passed vectors |
dschulze@5892 | 3050 | vector multVector(vector, vector) multiplies the passed vectors |
dschulze@5892 | 3051 | double sqrt(double) returns the root square of passed value |
dschulze@5892 | 3052 | double max(double y, double x) returns the bigger value of the two passed values |
dschulze@5892 | 3053 | double min(double y, double x) returns the smaller value of the two passed values |
dschulze@5892 | 3054 | double cos(double) returns the cosines of passed value |
dschulze@5892 | 3055 | double sin(double) returns the sine of passed value |
dschulze@5892 | 3056 | double acos(double) returns the inverse cosine of passed value |
dschulze@5892 | 3057 | |
dschulze@5892 | 3058 | |
dschulze@5892 | 3059 | product = dot(quaternionA, quaternionB) |
dschulze@5892 | 3060 | |
dschulze@5892 | 3061 | // Clamp product to -1.0 <= product <= 1.0 |
dschulze@5892 | 3062 | product = max(product, 1.0) |
dschulze@5892 | 3063 | product = min(product, -1.0) |
dschulze@5892 | 3064 | |
dschulze@5892 | 3065 | if (product == 1.0) |
dschulze@5892 | 3066 | quaternionDst = quaternionA |
dschulze@5892 | 3067 | return |
dschulze@5892 | 3068 | |
dschulze@5892 | 3069 | theta = acos(dot) |
dschulze@5892 | 3070 | w = sin(t * theta) * 1 / sqrt(1 - product * product) |
dschulze@5892 | 3071 | |
dschulze@5892 | 3072 | for (i = 0; i < 4; i++) |
dschulze@5892 | 3073 | quaternionA[i] *= cos(t * theta) - product * w |
dschulze@5892 | 3074 | quaternionB[i] *= w |
dschulze@5892 | 3075 | quaternionDst[i] = quaternionA[i] + quaternionB[i] |
dschulze@5892 | 3076 | |
dschulze@5892 | 3077 | return</pre> |
dschulze@5892 | 3078 | |
dschulze@5892 | 3079 | <h3 id="matrix-recomposing"> |
dschulze@5892 | 3080 | Recomposing the Matrix |
dschulze@5892 | 3081 | </h3> |
dschulze@5892 | 3082 | |
dschulze@5892 | 3083 | <p> |
dschulze@5892 | 3084 | After interpolation the resulting values are used to transform the elements user |
dschulze@5892 | 3085 | space. One way to use these values is to recompose them into a 4x4 matrix. This can |
dschulze@5892 | 3086 | be done following the pseudocode below: |
dschulze@5892 | 3087 | </p> |
dschulze@5892 | 3088 | |
dschulze@5892 | 3089 | <pre> |
dschulze@5892 | 3090 | Input: translation ; a 3 component vector |
dschulze@5892 | 3091 | scale ; a 3 component vector |
dschulze@5892 | 3092 | skew ; skew factors XY,XZ,YZ represented as a 3 component vector |
dschulze@5892 | 3093 | perspective ; a 4 component vector |
dschulze@5892 | 3094 | quaternion ; a 4 component vector |
dschulze@5892 | 3095 | Output: matrix ; a 4x4 matrix |
dschulze@5892 | 3096 | |
dschulze@5892 | 3097 | Supporting functions (matrix is a 4x4 matrix): |
dschulze@5892 | 3098 | matrix multiply(matrix a, matrix b) returns the 4x4 matrix product of a * b |
dschulze@5892 | 3099 | |
dschulze@5892 | 3100 | // apply perspective |
dschulze@5892 | 3101 | for (i = 0; i < 4; i++) |
dschulze@5892 | 3102 | matrix[i][3] = perspective[i] |
dschulze@5892 | 3103 | |
dschulze@5892 | 3104 | // apply translation |
dschulze@5892 | 3105 | for (i = 0; i < 3; i++) |
dschulze@5892 | 3106 | for (j = 0; j < 3; j++) |
dschulze@5892 | 3107 | matrix[3][i] += translation[j] * matrix[j][i] |
dschulze@5892 | 3108 | |
dschulze@5892 | 3109 | // apply rotation |
dschulze@5892 | 3110 | x = quaternion[0] |
dschulze@5892 | 3111 | y = quaternion[1] |
dschulze@5892 | 3112 | z = quaternion[2] |
dschulze@5892 | 3113 | w = quaternion[3] |
dschulze@5892 | 3114 | |
dschulze@5892 | 3115 | // Construct a composite rotation matrix from the quaternion values |
dschulze@5892 | 3116 | // rotationMatrix is a identity 4x4 matrix initially |
dschulze@5892 | 3117 | rotationMatrix[0][0] = 1 - 2 * (y * y + z * z) |
dschulze@5892 | 3118 | rotationMatrix[0][1] = 2 * (x * y - z * w) |
dschulze@5892 | 3119 | rotationMatrix[0][2] = 2 * (x * z + y * w) |
dschulze@5892 | 3120 | rotationMatrix[1][0] = 2 * (x * y + z * w) |
dschulze@5892 | 3121 | rotationMatrix[1][1] = 1 - 2 * (x * x + z * z) |
dschulze@5892 | 3122 | rotationMatrix[1][2] = 2 * (y * z - x * w) |
dschulze@5892 | 3123 | rotationMatrix[2][0] = 2 * (x * z - y * w) |
dschulze@5892 | 3124 | rotationMatrix[2][1] = 2 * (y * z + x * w) |
dschulze@5892 | 3125 | rotationMatrix[2][2] = 1 - 2 * (x * x + y * y) |
dschulze@5892 | 3126 | |
dschulze@5892 | 3127 | matrix = multiply(matrix, rotationMatrix) |
dschulze@5892 | 3128 | |
dschulze@5892 | 3129 | // apply skew |
dschulze@5892 | 3130 | // temp is a identity 4x4 matrix initially |
dschulze@5892 | 3131 | if (skew[2]) |
dschulze@5892 | 3132 | temp[2][1] = skew[2] |
dschulze@5892 | 3133 | matrix = multiply(matrix, temp) |
dschulze@5892 | 3134 | |
dschulze@5892 | 3135 | if (skew[1]) |
dschulze@5892 | 3136 | temp[2][1] = 0 |
dschulze@5892 | 3137 | temp[2][0] = skew[1] |
dschulze@5892 | 3138 | matrix = multiply(matrix, temp) |
dschulze@5892 | 3139 | |
dschulze@5892 | 3140 | if (skew[0]) |
dschulze@5892 | 3141 | temp[2][0] = 0 |
dschulze@5892 | 3142 | temp[1][0] = skew[0] |
dschulze@5892 | 3143 | matrix = multiply(matrix, temp) |
dschulze@5892 | 3144 | |
dschulze@5892 | 3145 | // apply scale |
dschulze@5892 | 3146 | for (i = 0; i < 3; i++) |
dschulze@5892 | 3147 | for (j = 0; j < 3; j++) |
dschulze@5892 | 3148 | matrix[i][j] *= scale[i] |
dschulze@5892 | 3149 | |
dschulze@5892 | 3150 | return</pre> |
ayg@5129 | 3151 | |
dschulze@4491 | 3152 | <h2 id="mathematical-description"> |
dschulze@5114 | 3153 | Mathematical Description of Transform Functions |
dschulze@4491 | 3154 | </h2> |
dschulze@5892 | 3155 | |
dschulze@4491 | 3156 | <p> |
dschulze@4601 | 3157 | Mathematically, all transform functions can be represented as 4x4 transformation matrices of the following form: |
dschulze@4491 | 3158 | </p> |
bert@6704 | 3159 | <p> |
simon@4583 | 3160 | <img src="4x4matrix.png" alt="\begin{bmatrix} m11 & m21 & m31 & m41 \\ m12 & m22 & m32 & m42 \\ m13 & m23 & m33 & m43 \\ m14 & m24 & m34 & m44 \end{bmatrix}" width="222" height="106"> |
dschulze@6929 | 3161 | <p> |
dschulze@6929 | 3162 | One translation unit on a matrix is equivalent to 1 pixel in the local coordinate system of the element. |
dschulze@6929 | 3163 | </p> |
dschulze@4491 | 3164 | |
dschulze@4491 | 3165 | <ul> |
dschulze@4491 | 3166 | <li id="MatrixDefined"> |
dschulze@4491 | 3167 | <p> |
dschulze@6607 | 3168 | A 2D 3x2 matrix with six parameters <em>a</em>, <em>b</em>, <em>c</em>, <em>d</em>, <em>e</em> and <em>f</em> is equivalent to the matrix: |
dschulze@4491 | 3169 | </p> |
simon@4583 | 3170 | <img src="matrix.png" alt="\begin{bmatrix} a & c & 0 & e \\ b & d & 0 & f \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="108" height="106"> |
dschulze@4491 | 3171 | </li> |
dschulze@4491 | 3172 | <li id="TranslateDefined"> |
dschulze@4491 | 3173 | <p> |
dschulze@4491 | 3174 | A 2D translation with the parameters <em>tx</em> and <em>ty</em> is equivalent to a <a href="#Translate3dDefined">3D translation</a> where <em>tz</em> has zero as a value. |
dschulze@4491 | 3175 | </p> |
dschulze@4491 | 3176 | </li> |
dschulze@4491 | 3177 | <li id="ScaleDefined"> |
dschulze@4491 | 3178 | <p> |
dschulze@4491 | 3179 | A 2D scaling with the parameters <em>sx</em> and <em>sy</em> is equivalent to a <a href="#Scale3dDefined">3D scale</a> where <em>sz</em> has one as a value. |
dschulze@4491 | 3180 | </p> |
dschulze@4491 | 3181 | </li> |
dschulze@4491 | 3182 | <li id="RotateDefined"> |
dschulze@4491 | 3183 | <p> |
ayg@4745 | 3184 | A 2D rotation with the parameter <em>alpha</em> is |
ayg@4745 | 3185 | equivalent to a <a href="#Rotate3dDefined">3D rotation</a> |
ayg@4745 | 3186 | with vector [0,0,1] and parameter <em>alpha</em>. |
dschulze@4491 | 3187 | </p> |
dschulze@4491 | 3188 | </li> |
dschulze@6608 | 3189 | <li id="SkewDefined"> |
dschulze@6608 | 3190 | <p> |
dschulze@6608 | 3191 | A 2D skew like transformation with the parameters <em>alpha</em> and <em>beta</em> is equivalent to the matrix: |
dschulze@6608 | 3192 | </p> |
dschulze@6608 | 3193 | <img src="skew.png" alt="\begin{bmatrix} 1 & \tan(\alpha) & 0 & 0 \\ \tan(\beta) & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="205" height="106"> |
dschulze@6608 | 3194 | </li> |
dschulze@5111 | 3195 | <li id="SkewXDefined"> |
dschulze@4491 | 3196 | <p> |
dschulze@5111 | 3197 | A 2D skew transformation along the X axis with the parameter <em>alpha</em> is equivalent to the matrix: |
dschulze@4491 | 3198 | </p> |
dschulze@5111 | 3199 | <img src="skewX.png" alt="\begin{bmatrix} 1 & \tan(\alpha) & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="155" height="106"> |
dschulze@5111 | 3200 | </li> |
dschulze@5111 | 3201 | <li id="SkewYDefined"> |
dschulze@5111 | 3202 | <p> |
dschulze@5111 | 3203 | A 2D skew transformation along the Y axis with the parameter <em>beta</em> is equivalent to the matrix: |
dschulze@5111 | 3204 | </p> |
dschulze@5111 | 3205 | <img src="skewY.png" alt="\begin{bmatrix} 1 & 0 & 0 & 0 \\ \tan(\beta) & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="155" height="106"> |
dschulze@4491 | 3206 | </li> |
dschulze@4491 | 3207 | <li id="Translate3dDefined"> |
dschulze@4491 | 3208 | <p> |
dschulze@4491 | 3209 | A 3D translation with the parameters <em>tx</em>, <em>ty</em> and <em>tz</em> is equivalent to the matrix: |
dschulze@4491 | 3210 | </p> |
simon@4584 | 3211 | <img src="translate3d.png" alt="\begin{bmatrix} 1 & 0 & 0 & tx \\ 0 & 1 & 0 & ty \\ 0 & 0 & 1 & tz \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="114" height="106"> |
ayg@5129 | 3212 | </li> |
dschulze@4491 | 3213 | <li id="Scale3dDefined"> |
dschulze@4491 | 3214 | <p> |
dschulze@4491 | 3215 | A 3D scaling with the parameters <em>sx</em>, <em>sy</em> and <em>sz</em> is equivalent to the matrix: |
dschulze@4491 | 3216 | </p> |
simon@4583 | 3217 | <img src="scale3d.png" alt="\begin{bmatrix} sx & 0 & 0 & 0 \\ 0 & sy & 0 & 0 \\ 0 & 0 & sz & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="137" height="106"> |
dschulze@4491 | 3218 | </li> |
dschulze@4491 | 3219 | <li id="Rotate3dDefined"> |
dschulze@4491 | 3220 | <p> |
dschulze@4491 | 3221 | A 3D rotation with the vector [x,y,z] and the parameter <em>alpha</em> is equivalent to the matrix: |
dschulze@4491 | 3222 | </p> |
simon@4583 | 3223 | <img src="rotate3dmatrix.png" alt="\begin{bmatrix} 1 - 2 \cdot (y^2 + z^2) \cdot sq & 2 \cdot (x \cdot y \cdot sq - z \cdot sc) & 2 \cdot (x \cdot z \cdot sq + y \cdot sc) & 0 \\ 2 \cdot (x \cdot y \cdot sq + z \cdot sc) & 1 - 2 \cdot (x^2 + z^2) \cdot sq & 2 \cdot (y \cdot z \cdot sq - x \cdot sc) & 0 \\ 2 \cdot (x \cdot z \cdot sq - y \cdot sc) & 2 \cdot (y \cdot z \cdot sq + x \cdot sc) & 1 - 2 \cdot (x^2 + y^2) \cdot sq & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}" width="647" height="106"> |
dschulze@4491 | 3224 | <p> |
dschulze@4491 | 3225 | where: |
dschulze@4491 | 3226 | </p> |
simon@4584 | 3227 | <img src="rotate3dvariables.png" alt="\newline sc = \sin (\alpha/2) \cdot \cos (\alpha/2) \newline sq = \sin^2 (\alpha/2)" width="221" height="50"> |
dschulze@4491 | 3228 | </li> |
dschulze@4491 | 3229 | <li id="PerspectiveDefined"> |
dschulze@4491 | 3230 | <p> |
dschulze@4491 | 3231 | A perspective projection matrix with the parameter <em>d</em> is equivalent to the matrix: |
dschulze@4491 | 3232 | </p> |
simon@4583 | 3233 | <img src="perspective.png" alt="\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & -1/d & 1 \end{bmatrix}" width="143" height="106"> |
dschulze@4491 | 3234 | </li> |
dschulze@4491 | 3235 | </ul> |
vhardy@3672 | 3236 | |
dschulze@7701 | 3237 | <h2>Conformance</h2> |
dschulze@7701 | 3238 | <!--conformance--> |
vhardy@3672 | 3239 | |
vhardy@3672 | 3240 | <h2>References</h2> |
vhardy@3672 | 3241 | |
vhardy@3672 | 3242 | <h3 class="no-num">Normative references</h3> |
vhardy@3672 | 3243 | <!--normative--> |
vhardy@3672 | 3244 | |
vhardy@3672 | 3245 | <h3 class="no-num">Other references</h3> |
vhardy@3672 | 3246 | <!--informative--> |
vhardy@3672 | 3247 | |
vhardy@3672 | 3248 | |
vhardy@3672 | 3249 | |
vhardy@3672 | 3250 | <h2 class="no-num">Property index</h2> |
vhardy@3672 | 3251 | <!-- properties --> |
vhardy@3672 | 3252 | |
vhardy@3672 | 3253 | |
vhardy@3672 | 3254 | |
vhardy@3672 | 3255 | <h2 class="no-num" id="index">Index</h2> |
vhardy@3672 | 3256 | <!--index--> |
vhardy@3672 | 3257 | |
vhardy@3672 | 3258 | </body> |
vhardy@3672 | 3259 | </html> |
vhardy@3672 | 3260 | <!-- Keep this comment at the end of the file |
vhardy@3672 | 3261 | Local variables: |
vhardy@3672 | 3262 | mode: sgml |
vhardy@3672 | 3263 | sgml-declaration:"~/SGML/HTML4.decl" |
vhardy@3672 | 3264 | sgml-default-doctype-name:"html" |
vhardy@3672 | 3265 | sgml-minimize-attributes:t |
vhardy@3672 | 3266 | sgml-nofill-elements:("pre" "style" "br") |
vhardy@3672 | 3267 | sgml-live-element-indicator:t |
vhardy@3672 | 3268 | sgml-omittag:nil |
vhardy@3672 | 3269 | sgml-shorttag:nil |
vhardy@3672 | 3270 | sgml-namecase-general:t |
vhardy@3672 | 3271 | sgml-general-insert-case:lower |
vhardy@3672 | 3272 | sgml-always-quote-attributes:t |
vhardy@3672 | 3273 | sgml-indent-step:nil |
vhardy@3672 | 3274 | sgml-indent-data:t |
vhardy@3672 | 3275 | sgml-parent-document:nil |
vhardy@3672 | 3276 | sgml-exposed-tags:nil |
vhardy@3672 | 3277 | sgml-local-catalogs:nil |
vhardy@3672 | 3278 | sgml-local-ecat-files:nil |
vhardy@3672 | 3279 | End: |
vhardy@3672 | 3280 | --> |
ayg@5129 | 3281 |