1. 介绍
本节不具规范性。
CSS 视觉格式化模型描述了每个元素所在的坐标系。可以认为该坐标空间中的位置和尺寸以像素表示,从原点开始,正值向右和向下延伸。
此坐标空间可以通过transform属性进行修改。使用 transform,元素可以进行平移、旋转和缩放。
1.1. 模块交互
本模块定义了一组影响应用了这些属性的元素的视觉渲染的 CSS 属性;这些效果在根据视觉格式化模型([CSS2])确定元素的大小和位置后应用。这些属性的某些值会导致创建一个包含块,并/或创建一个堆叠上下文。
变换影响具有fixed值的background-attachment属性的元素的背景渲染,该属性在[CSS3BG]中进行了规范。
变换影响由元素接口扩展返回的客户端矩形,getClientRects()和getBoundingClientRect()在[CSSOM-VIEW]中进行了定义。
变换影响可滚动溢出区域的计算,详见[CSS-OVERFLOW-3]。
1.2. CSS 值
本规范遵循CSS 属性定义惯例,参见[CSS2]。 在本规范中未定义的值类型在 CSS 值与单位中定义,参见[CSS-VALUES-3]。 其他 CSS 模块可能会扩展这些值类型的定义。
除了在其定义中列出的特定属性值外, 本规范定义的所有属性 也接受CSS 全局关键字作为属性值。 为了可读性,未显式重复它们。
2. 术语
在本规范中使用时,术语具有本节中分配的含义。
- 可变换元素
-
可变换元素属于以下类别之一:
- 变换元素
-
一个元素的 transform 属性的计算值不是 none。
- 用户坐标系统
- 本地 坐标系统
-
通常,坐标系统定义了当前画布上的位置和距离。 当前本地坐标系统(也是用户坐标系统)是当前活动的坐标系统, 用于定义如何在当前画布上定位和计算坐标和长度。 当前用户坐标系统的原点位于由参考框指定的左上角,该参考框由 transform-box 属性指定。百分比值相对于此参考框的尺寸。 一个单位等于一个 CSS 像素。
- 变换矩阵
-
定义从一个坐标系统到另一个坐标系统的数学映射的矩阵。它由 transform 和 transform-origin 属性的值计算得出,如下文所述。
- 当前变换矩阵 (CTM)
- 二维矩阵
-
一个 3x2 变换矩阵,或一个 4x4 矩阵,其中 m31、m32、 m13、m23、m43、m14、m24、m34 等于 0,而 m33、m44 等于 1。
- 单位变换函数
-
一个变换函数,等同于一个单位 4x4 矩阵(参见变换函数的数学描述)。例如,单位变换函数包括 translate(0)、translateX(0)、translateY(0)、scale(1)、scaleX(1)、scaleY(1)、rotate(0)、skew(0, 0)、skewX(0)、skewY(0) 和 matrix(1, 0, 0, 1, 0, 0)。
- 后乘
- 后乘
-
项 A 后乘项 B 等于 A · B。
- 前乘
- 前乘
-
项 A 前乘项 B 等于 B · A。
- 乘
-
项 A 乘以项 B 等于 A · B。
3. 变换渲染模型
本节是规范性的。
为 transform 属性指定一个非 none 的值,会在应用该属性的元素上建立一个新的 本地坐标系统。从元素原本渲染的位置到该本地坐标系统的映射由元素的 变换矩阵给出。
变换矩阵通过以下方式从 transform 和 transform-origin 属性计算得出:
-
从单位矩阵开始。
-
按 transform-origin 计算的 X 和 Y 值进行平移。
-
从左到右依次乘以 transform 属性中的每一个变换函数。
-
按 transform-origin 计算的 X 和 Y 值的相反值进行平移。
一个元素的 transform 属性不是 none。
div {
transform-origin : 0 0 ;
transform : translate ( -10 px , -20 px ) scale ( 2 ) rotate ( 45 deg );
}
transform-origin 属性设置为 0 0,可以省略。变换矩阵 TM 通过将 <translate()>、<scale()> 和 <rotate()> <transform-function> 后乘计算得出。
变换适用于 可变换元素。
坐标空间是一个具有两个轴的坐标系:X 轴水平向右增加,Y 轴垂直向下增加。
变换是累积的。也就是说,元素在其父元素的坐标系统内建立自己的本地坐标系统。
要将坐标对 xlocal 和 ylocal 的点 plocal 从元素的 本地坐标系统映射到父级的坐标系统,后乘元素的 变换矩阵 TM 和 plocal。结果是父级坐标系统中的映射点 pparent,其坐标对为 xparent 和 yparent。
从用户的角度来看,元素有效地累积了其祖先的所有 transform 属性,以及应用于其本地的任何变换。这些变换的累积定义了元素的 当前变换矩阵 (CTM)。
当前变换矩阵通过从 视口坐标系统开始,最终通过元素的 变换矩阵 后乘计算得出。
< svg xmlns = "http://www.w3.org/2000/svg" >
< g transform = "translate(-10, 20)" >
< g transform = "scale(2)" >
< rect width = "200" height = "200" transform = "rotate(45)" />
</ g >
</ g >
</ svg >
-
translate(-10, 20) 计算为变换矩阵 T1
-
scale(2) 计算为变换矩阵 T2
-
rotate(45) 计算为变换矩阵 T3
SVG rect 元素的当前变换矩阵 (CTM) 是依次乘以 T1、T2 和 T3 的结果。
要将坐标对 xlocal 和 ylocal 的点 plocal 从 SVG rect 元素的 本地坐标系统 映射到 视口坐标系统,将元素的 当前变换矩阵 CTM 与 plocal 进行后乘。结果是视口坐标系统中的映射点 pviewport,其坐标对为 xviewport 和 yviewport。
注意: 变换会影响视觉渲染,但除了影响溢出之外,对 CSS 布局没有影响。变换也会在计算通过 Element 接口扩展暴露的客户端矩形时被考虑,主要是 getClientRects() 和 getBoundingClientRect(),它们由 [CSSOM-VIEW] 规范定义。
div {
height : 100 px ;
width : 100 px ;
transform-origin : 50 px 50 px ;
transform : rotate ( 45 deg );
}
transform-origin 属性将原点在 X 和 Y 方向上分别移动 50 像素。变换将元素围绕原点顺时针旋转 45°。在应用了所有变换函数后,原点的平移将按相反的 X 和 Y 方向平移回 -50 像素。
div {
height : 100 px ;
width : 100 px ;
transform : translate ( 80 px , 80 px ) scale ( 1.5 , 1.5 ) rotate ( 45 deg );
}
视觉效果上看,div 元素先向左下方向平移 80px,然后放大 150%,最后旋转 45°。
每个 <transform-function> 都可以用一个对应的 4x4 矩阵表示。为了将点从 div 盒子的坐标空间映射到父元素的坐标空间,变换按相反顺序进行矩阵相乘:
详细信息请参见 变换函数列表。
注意: 可以通过嵌套具有等效变换的元素来获得相同的渲染效果:
< div style = "transform: translate(80px, 80px)" >
< div style = "transform: scale(1.5, 1.5)" >
< div style = "transform: rotate(45deg)" ></ div >
</ div >
</ div >
对于布局受 CSS 盒模型控制的元素,transform
属性不会影响变换元素周围内容的流动。然而,溢出区域的范围会考虑变换后的元素。这种行为类似于通过相对定位偏移元素时的情况。因此,如果
overflow 属性的值是 scroll 或 auto,将会出现滚动条以查看在可见区域之外被变换的内容。特别地,变换可以扩展(但不会缩小)溢出区域的大小,溢出区域的大小是应用变换前后元素边界的并集。
对于布局受 CSS 盒模型控制的元素,任何非 none 的 transform
属性值都会导致创建一个堆叠上下文。实现必须在其父级堆叠上下文中按照与如果它是带有 z-index:
0 的定位元素相同的堆叠顺序来绘制它创建的层。如果带有 transform
的元素是定位的,那么 z-index 属性按 [CSS2] 中的描述生效,除非 auto 被视为 0,因为总是会创建一个新的堆叠上下文。
对于布局受 CSS 盒模型控制的元素,任何非 none 的 transform 属性值还会导致元素为所有后代建立一个 包含块。其填充框将用于布局所有绝对定位的后代、固定定位的后代以及后代中的固定背景附件。
< style >
# container {
width : 300 px ;
height : 200 px ;
border : 5 px dashed black ;
padding : 5 px ;
overflow : scroll ;
}
# bloat {
height : 1000 px ;
}
# child {
right : 0 ;
bottom : 0 ;
width : 10 % ;
height : 10 % ;
background : green ;
}
</ style >
< div id = "container" style = "transform:translateX(5px);" >
< div id = "bloat" ></ div >
< div id = "child" style = "position:fixed;" ></ div >
</ div >
versus
< div id = "container" style = "position:relative; z-index:0; left:5px;" >
< div id = "bloat" ></ div >
< div id = "child" style = "position:absolute;" ></ div >
</ div >
固定背景 在根元素上会受到为该元素指定的任何变换的影响。对于所有其他受到变换影响的元素(即对它们或其任何祖先元素应用了变换的元素),fixed 值的 background-attachment 属性将被视为其值为 scroll。background-attachment 的计算值不受影响。
注意: 如果根元素被变换,变换将应用于整个画布,包括为根元素指定的任何背景。由于根元素的背景绘制区域是整个画布,而画布是无限的,变换可能会导致原本不在屏幕上的背景部分出现。例如,如果根元素的背景是重复的点,并且为根元素指定了 scale(0.5) 的变换,点的大小会缩小一半,但数量会翻倍,因此它们仍然覆盖整个视口。
4. The transform Property
通过 transform 属性,对元素渲染的坐标系应用变换。该属性包含一个 变换函数 列表。坐标系的最终变换值通过将列表中的每个函数转换为其对应的矩阵(如变换函数的数学描述所定义),然后将这些矩阵相乘得出。
名称: | transform |
---|---|
值: | none | <transform-list> |
初始值: | none |
应用于: | 可变换元素 |
继承: | no |
百分比: | 相对于 参考框 的尺寸 |
计算值: | 按指定计算,但长度为绝对值 |
语法顺序: | 根据语法 |
动画类型: | 变换列表,详见插值规则 |
任何计算值为 none 以外的 transform 都会影响包含块和堆叠上下文,如 §3 变换渲染模型 中所述。
<transform-list> = <transform-function>+
4.1. 变换函数的序列化
为了序列化 <transform-function>,根据各自的语法顺序序列化,尽量避免 <calc()> 表达式,在不改变含义的情况下省略组件,用单个空格连接空格分隔的标记,并在每个序列化的逗号后跟一个空格。
4.2. 计算值的序列化
计算值的 <transform-list> 通过以下算法序列化为一个 <matrix()> 函数:
-
让 transform 是一个初始化为单位矩阵的 4x4 矩阵。矩阵元素 m11, m22, m33 和 m44 必须设置为 1,其余元素必须设置为 0。
-
将 <transform-function> 在 <transform-list> 中的每一项进行后乘以 transform。
-
将 transform 序列化为 <matrix()> 函数。
5. The transform-origin Property
名称: | transform-origin |
---|---|
值: | [ left | center
| right | top | bottom | <length-percentage> ] | [ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ] <length>? | [[ center | left | right ] && [ center | top | bottom ]] <length>? |
初始值: | 50% 50% |
应用于: | 可变换元素 |
继承: | no |
百分比: | 相对于 参考框 的尺寸 |
计算值: | 参见 background-position |
语法顺序: | 根据语法 |
动画类型: | 按计算值 |
属性 transform 和 transform-origin 的值用于计算 变换矩阵,如上所述。
如果只指定了一个值,则第二个值假定为 center。如果指定了一个或两个值,则假定第三个值为 0px。
如果定义了两个或更多的值,且没有值是关键字,或者唯一使用的关键字是 center,则第一个值表示水平位置(或偏移),第二个表示垂直位置(或偏移)。第三个值始终表示 Z 位置(或偏移),必须为 <length> 类型。
- <length-percentage>
-
水平偏移的百分比相对于 参考框 的宽度,垂直偏移的百分比相对于 参考框 的高度。水平和垂直偏移的值表示从 参考框 左上角的偏移量。
- <length>
-
长度值提供固定的偏移量。水平和垂直偏移的值表示从 参考框 左上角的偏移量。
- top
-
垂直位置计算为 0%。
- right
-
水平位置计算为 100%。
- bottom
-
垂直位置计算为 100%。
- left
-
水平位置计算为 0%。
- center
-
水平位置计算为 50%(left 50%),如果未指定水平位置;垂直位置则计算为 50%(top 50%),如果未指定垂直位置。
对于没有关联 CSS 布局框的 SVG 元素,初始 使用值 为 0 0,就像用户代理样式表中包含以下内容:
*:not(svg), *:not(foreignObject) > svg {
transform-origin : 0 0 ;
}
属性 transform-origin 是 已解析值特殊情况属性,类似于 height。[CSSOM]
6. Transform reference box: the transform-box property
名称: | transform-box |
---|---|
值: | content-box | border-box | fill-box | stroke-box | view-box |
初始值: | view-box |
应用于: | 可变换元素 |
继承: | no |
百分比: | N/A |
计算值: | 指定的关键字 |
语法顺序: | 根据语法 |
动画类型: | 离散 |
通过 transform 和 transform-origin 属性定义的所有变换均相对于元素的 参考框 的位置和尺寸进行。参考框由以下之一指定:
- content-box
-
使用内容框作为参考框。表格的参考框是其 表格包装框 的边框框,而不是其表格框。
- border-box
-
使用边框框作为参考框。表格的参考框是其 表格包装框 的边框框,而不是其表格框。
- fill-box
-
使用 对象边界框 作为参考框。
- stroke-box
-
使用 描边边界框 作为参考框。
- view-box
-
使用最近的 SVG 视口 作为参考框。
对于 SVG
pattern
元素,参考框由
patternUnits
属性定义 [SVG2]。
对于 SVG linearGradient 和 radialGradient 元素,参考框由
gradientUnits
属性定义 [SVG2]。
对于 SVG clipPath 元素,参考框由
clipPathUnits
属性定义 [CSS-MASKING]。
参考框为 transform-origin 属性指定的原点增加了一个额外的偏移量。
对于没有关联 CSS 布局框的 SVG 元素,使用值 为 content-box 为 fill-box,并且 border-box 为 stroke-box。
对于有关联 CSS 布局框的元素,使用值 为 fill-box 为 content-box,并且 stroke-box 和 view-box 为 border-box。
7. SVG transform 属性
7.1. SVG 表现属性
transform-origin CSS 属性也是一个表现属性,扩展了现有的表现属性列表 [SVG2]。
SVG 2 定义了 transform、patternTransform
、
gradientTransform
属性作为表现属性,由 CSS 的 transform 属性表示 [SVG2]。
在 CSS 层叠中参与的优先级由 SVG 规范中的表现属性确定。根据 SVG,用户代理概念上为表现属性插入一个 新的作者样式表,它是作者样式表集合中的第一个 [SVG2]。
<svg xmlns= "http://www.w3.org/2000/svg" >
<style>
.container {
transform: translate(100px, 100px);
}
</style>
<g class= "container" transform= "translate(200 200)" >
<rect width= "100" height= "100" fill= "blue" />
</g>
</svg>
由于参与 CSS 层叠,transform 样式属性覆盖了 transform 属性。因此,容器被水平和垂直方向上分别平移了 100px,而不是 200px。
7.2. SVG transform 属性的语法
为了向后兼容,transform、
patternTransform
、
gradientTransform
属性的语法与 transform CSS 属性的语法不同。对于这些属性,不支持为 CSS 定义的附加
<transform-function>。
具体来说,<translateX()>、<translateY()>、<scaleX()>、<scaleY()> 和 <skew()> 不支持 transform、
patternTransform
、
gradientTransform
属性。
以下列表使用巴科斯-瑙尔范式(BNF)来定义 transform、patternTransform 和 gradientTransform 属性的值,接着是信息性铁路图。以下符号表示:
-
*:0 或更多
-
+:1 或更多
-
?:0 或 1
-
():分组
-
|:表示选择
-
双引号包围字面量。字面量由字母、左括号和右括号组成[CSS-SYNTAX-3]。
-
<number-token> 由 CSS 语法模块定义[CSS-SYNTAX-3]。
注意:此语法反映了用户代理中的实现行为,与 SVG 1.1 定义的语法不同。
- 左括号 (
- U+0028 左括号
- 右括号 )
- U+0029 右括号
- 逗号
- U+002C 逗号。
- 空白字符
- 可以是 U+000A 换行符、U+000D 回车符、U+0009 水平制表符,或 U+0020 空格。
- comma-wsp
-
(wsp+ comma? wsp*) | (comma wsp*)
- translate
-
"translate" wsp* "(" wsp* number ( comma-wsp? number )? wsp* ")"
- scale
-
"scale" wsp* "(" wsp* number ( comma-wsp? number )? wsp* ")"
- rotate
-
"rotate" wsp* "(" wsp* number ( comma-wsp? number comma-wsp? number )? wsp* ")"
- skewX
-
"skewY" wsp* "(" wsp* number wsp* ")"
- skewY
-
"skewY" wsp* "(" wsp* number wsp* ")"
- matrix
-
"matrix" wsp* "(" wsp* number comma-wsp? number comma-wsp? number comma-wsp? number comma-wsp? number comma-wsp? number wsp* ")"
- transform
-
matrix | translate | scale | rotate | skewX | skewY
- transforms
-
transform | transform comma-wsp transforms
- transform-list
-
wsp* transforms? wsp*
7.3. SVG 变换函数
根据上述语法定义的 transform、
patternTransform
、
gradientTransform
属性的 SVG 变换函数映射到 CSS <transform-function> 如下:
SVG 变换函数 | CSS <transform-function> | 附加说明 |
---|---|---|
translate | <translate()> | 数值按 CSS <length> 类型解释,单位为 px。 |
scale | <scale()> | |
rotate | <rotate()> | 仅单值版本。数值按 CSS <angle> 类型解释,单位为 deg。 |
skewX | <skewX()> | 数值按 CSS <angle> 类型解释,单位为 deg。 |
skewY | <skewY()> | 数值按 CSS <angle> 类型解释,单位为 deg。 |
matrix | <matrix()> |
带有 3 个值的 SVG rotate 变换函数无法映射到对应的 CSS <transform-function>。这两个可选的数值表示水平平移值 cx 和垂直平移值 cy。这两个数值按 CSS <length> 类型解释,单位为 px,并定义旋转的原点。该行为相当于由 cx、cy 进行初始平移,然后进行由第一个数值定义的 <angle> 类型的旋转,单位为 deg,最后由 -cx、-cy 进行平移。
transform 属性可以作为 CSS 过渡的起始值或结束值。如果 transform 属性的值是 CSS 过渡的起始值或结束值,并且 SVG 变换列表 中至少包含一个带有 3 个值的 rotate 变换函数,则各个 SVG 变换函数必须进行 后乘,并且结果矩阵必须映射为 <matrix()> CSS <transform-function>,并作为 CSS 过渡的起始/结束值使用。
7.4. 用户坐标空间
对于
pattern
元素,
patternTransform
属性和 transform 属性在图案坐标系统中定义了一个额外的变换。参见
patternUnits
属性了解详细信息 [SVG2]。
对于
linearGradient
和
radialGradient
元素,
gradientTransform
属性和 transform 属性在渐变坐标系统中定义了一个额外的变换。参见
gradientUnits
属性了解详细信息 [SVG2]。
对于
clipPath
元素, transform 属性和 transform 属性在剪切路径坐标空间中定义了一个额外的变换。参见
clipPathUnits
属性了解详细信息 [CSS-MASKING]。
对于所有其他 可变换元素,transform 属性和 transform 属性定义了父元素当前用户坐标系统中的变换。transform 属性的所有百分比值相对于元素的 参考框。
在以下示例中,图案上的 transform-origin 属性指定了原点在水平和垂直方向上的 50% 平移。transform 属性也指定了一个平移,但使用的是绝对长度。
<svg xmlns= "http://www.w3.org/2000/svg" >
<style>
pattern {
transform: rotate(45deg);
transform-origin: 50% 50%;
}
</style>
<defs>
<pattern id= "pattern-1" >
<rect id= "rect1" width= "100" height= "100" fill= "blue" />
</pattern>
</defs>
<rect width= "200" height= "200" fill= "url(#pattern-1)" />
</svg>
SVG
pattern
元素没有边界框。引用的
rect
元素的 参考框 被用来解决 transform-origin 属性的相对值。因此,原点会暂时平移 100 像素以旋转
pattern
元素内容的用户空间。
7.5. SVG DOM接口用于 transform 属性
SVG 规范在 SVG DOM 中定义了 "SVGAnimatedTransformList"
接口,以提供对 SVG transform、
gradientTransform
和
patternTransform
属性的动画值和基本值的访问。为了确保向后兼容,用户代理必须继续支持此API。
baseVal
允许作者访问和修改 SVG transform、
patternTransform
、
gradientTransform
属性的值。为了提供对 SVG DOM 的必要向后兼容性,baseVal
必须反映此作者样式表的值。对
baseVal
的所有修改都必须立即影响该作者样式表。
animVal
代表 transform 属性的计算样式。因此,它包括所有应用的 CSS3 过渡、CSS3 动画 或 SVG
动画,如果有正在进行的任何动画。animVal
的计算样式和 SVG DOM 对象无法修改。
8. SVG 动画
8.1.
animate
和
set
元素
根据本规范,
animate
元素和
set
元素可以对数据类型 <transform-list> 进行动画处理。
由于 <transform-list> 动画的特定行为,动画效果是相对于基础值 后乘 的,而不是添加到基础值中。
From-to、from-by 和 by 动画在 SMIL 中定义为与相应的 values 动画等效。然而,to 动画是加性行为和非加性行为的混合 [SMIL3]。
To 动画为
animate
元素提供了从基础值平滑过渡到 to 属性值的特定功能,这在数学上与加性变换动画需要进行 后乘 的要求相冲突。因此,针对
animate
元素的 to 动画行为是未定义的。建议作者使用 from-to、from-by、by 或 values
动画来实现所需的变换动画。
数据类型为 <transform-list> 的动画,其
calcMode
属性值 "paced" 未定义。如果指定,用户代理可能会选择值 "linear"
来代替。未来版本的规范可能会定义如何在 <transform-list>
上执行 paced 动画。
注意: 以下段落扩展了 可动画的元素、属性和属性 [SVG11]。
引入的展示属性
transform、
patternTransform
、
gradientTransform
和
transform-origin 是可动画的。
根据本规范,SVG 基本数据类型 <transform-list> 等同于一系列 <transform-function>。<transform-list> 是可动画和加性的。该数据类型可以使用 SVG
animate
元素和 SVG
set
元素进行动画处理。SVG 动画必须遵循 变换值之间的过渡和动画 部分中描述的相同动画步骤。
数据类型 | 加性? | animate
|
set
|
animateColor
|
animateTransform
|
备注 |
---|---|---|---|---|---|---|
<transform-list> | 是 | 是 | 是 | 否 | 是 | 对于
animateTransform ,加性表示该变换是相对于基础变换集合
后乘 的。
|
8.2. 加法的中性元素
某些动画需要加法的中性元素。对于变换函数,这些中性元素是标量或标量列表,值为 0。变换函数的中性元素示例包括 translate(0)、scale(0)、rotate(0)、skewX(0)、skewY(0)。
注意: 该段落聚焦于 [SMIL] 的要求及 [SVG11] 中定义的扩展。本规范未为上面列出的函数以外的其他变换函数提供中性元素的定义。
一个 by 动画,by 值为 vb,等同于使用两个值的动画值列表,第一个值为目标属性域的加法中性元素(记为 0),第二个值为 vb,且 additive="sum"。[SMIL3]
<rect width= "100" height= "100" >
<animateTransform attributeName= "transform" attributeType= "XML"
type= "scale" by= "1" dur= "5s" fill= "freeze" />
</rect>
当执行 by 动画且 type="scale" 时,加法的中性元素值为 0。因此,执行上面示例中的动画会导致矩形在 0 秒时不可见(因为动画变换列表值为 scale(0)),并在 5 秒时恢复到其原始大小(因为动画变换列表值为 scale(1))。
8.3. SVG 1.1 'attributeName' 属性
SVG 1.1 动画 定义了 "attributeName"
属性,用于指定目标属性的名称。对于展示属性
gradientTransform
和
patternTransform
,也可以使用值
transform。相同的 transform 属性将被动画化。
在此示例中,线性渐变的渐变变换被动画化。
<linearGradient gradientTransform= "scale(2)" >
<animate attributeName= "gradientTransform" from= "scale(2)" to= "scale(4)"
dur= "3s" additive= "sum" />
<animate attributeName= "transform" from= "translate(0, 0)" to= "translate(100px, 100px)"
dur= "3s" additive= "sum" />
</linearGradient>
linearGradient
元素指定了
gradientTransform
展示属性。两个
animate
元素分别针对目标属性
gradientTransform
和 transform。即使所有动画都应用于相同的渐变变换,它们会先应用第一次动画的缩放,然后应用第二次动画的平移。
9. 变换函数
transform 属性的值是 <transform-function> 的列表。下方列出了允许的变换函数集合。对于以下函数,<zero> 的行为与 0deg 相同(无单位的 0 角度为了兼容性被保留)。对于水平平移的百分比相对于 参考框 的宽度计算。对于垂直平移的百分比相对于 参考框 的高度计算。
9.1. 二维变换函数
- matrix() = matrix( <number> [, <number> ]{5,5} )
-
指定了六个值 a, b, c, d, e, f 形式的 二维变换矩阵。
- translate() = translate( <length-percentage> [, <length-percentage> ]? )
-
指定了通过向量 [tx, ty] 的 二维平移,其中 tx 是第一个平移值参数,ty 是可选的第二个平移值参数。如果未提供 <ty>,则 ty 的值为 0。
- translateX() = translateX( <length-percentage> )
-
指定了沿 X 轴的 平移。
- translateY() = translateY( <length-percentage> )
-
指定了沿 Y 轴的 平移。
- scale() = scale( <number> [, <number> ]? )
-
指定了 [sx,sy] 缩放向量的 二维缩放 操作。如果未提供第二个参数,则其值等于第一个参数。例如,scale(1, 1) 将保持元素不变,而 scale(2, 2) 将使其在 X 和 Y 轴上长度变为原来的两倍,或者其几何尺寸变为原来的四倍。
- scaleX() = scaleX( <number> )
-
指定了使用 [sx,1] 缩放向量的 二维缩放 操作,其中 sx 是参数。
- scaleY() = scaleY( <number> )
-
指定了使用 [1,sy] 缩放向量的 二维缩放 操作,其中 sy 是参数。
- rotate() = rotate( [ <angle> | <zero> ] )
-
指定了元素围绕其 transform-origin 属性定义的原点按参数指定的角度进行的 二维旋转。例如,rotate(90deg) 会使元素顺时针旋转四分之一圈。
- skew() = skew( [ <angle> | <zero> ] [, [ <angle> | <zero> ] ]? )
-
指定了 [ax,ay] 的 二维扭曲。如果未提供第二个参数,则其值为 0。
skew() 存在是为了兼容性原因,不应在新的内容中使用。应改用 skewX() 或 skewY(),注意 skew() 的行为与 skewX() 和 skewY() 相乘的结果不同。
- skewX() = skewX( [ <angle> | <zero> ] )
-
指定了沿 X 轴的 二维扭曲变换。
- skewY() = skewY( [ <angle> | <zero> ] )
-
指定了沿 Y 轴的 二维扭曲变换。
9.2. 变换函数原语和派生函数
一些变换函数可以由更通用的变换函数表示。这些变换函数称为派生变换函数,而通用变换函数称为原语变换函数。二维原语及其派生变换函数如下:
- translate()
- 用于 <translateX()>、<translateY()> 和 <translate()>。
- scale()
- 用于 <scaleX()>、<scaleY()> 和 <scale()>。
10. 变换函数列表
如果提供了一组 <transform-function>,则其净效应与按提供的顺序单独指定每个变换函数相同。
也就是说,在没有其他影响位置和尺寸的样式时,嵌套的一组变换等效于一组变换函数列表,从祖先的坐标系应用到给定元素的本地坐标系。结果变换是变换列表的矩阵相乘。
< div style = "transform: translate(-10px, -20px) scale(2) rotate(45deg)" />
在功能上等同于:
< div style = "transform: translate(-10px, -20px)" id = "root" >
< div style = "transform: scale(2)" >
< div style = "transform: rotate(45deg)" >
</ div >
</ div >
</ div >
如果变换函数导致对象的当前变换矩阵不可逆,则对象及其内容不会显示。
以下示例中的对象被缩放为 0。
< style >
. box {
transform : scale( 0 );
}
</ style >
< div class = "box" >
Not visible
</ div >
缩放导致 div 框的坐标空间生成了不可逆的 CTM。因此,div 框及其内部的文本均不会显示。
11. 变换的插值
变换函数列表 的插值按照如下方式执行:
-
如果 Va 和
Vb 都是 none:
-
Vresult 为 none。
-
-
将 none
视为长度为零的列表,
如果 Va 或 Vb 的长度不同:
-
将较短的列表扩展为较长列表的长度,将每个额外位置的函数设置为匹配较长列表中相应位置的恒等变换函数。然后按照下一个规则插值两个变换函数列表。
-
-
令 Vresult 为一个空列表。从 Va 和 Vb
的开头开始,比较每个位置的对应函数:
-
当函数具有相同的名称,或者是相同的原语变换函数的派生时,按照 §12 原语和派生变换函数的插值 中描述的方式插值对应的函数对,并将结果追加到 Vresult。
-
如果该对没有共同的名称或原语变换函数,分别将 Va 和 Vb 中其余的变换函数后乘生成两个 4x4 矩阵。按 插值,按照 §13 矩阵的插值 中描述的方式对这两个矩阵进行插值,并将结果追加到 Vresult,然后停止迭代 Va 和 Vb。
例如,如果 Va 是 rotate(0deg) scale(1) translate(20px),而 Vb 是 rotate(270deg) translate(10px) scale(2),则 rotate(0deg) 和 rotate(360deg) 函数将按照 §12 原语和派生变换函数的插值 进行插值,而每个列表其余部分——scale(1) translate(20px) 和 translate(10px) scale(2)——将首先转换为等效的 4x4 矩阵,然后按照 §13 矩阵的插值 描述的方式进行插值。 以前的规范版本在除非列表中所有函数都匹配的情况下,不会尝试插值匹配的变换函数对。因此,在这个示例中,这两个列表将仅使用矩阵插值,并且第二个列表的 rotate(360deg) 组件将会丢失。
-
在某些情况下,动画可能会导致变换矩阵变为奇异或不可逆。例如,动画从比例为 1 变化为 -1。当矩阵处于这种状态时,变换的元素不会被渲染。
12. 原语和派生变换函数的插值
两个具有相同名称和相同参数数量的变换函数会直接进行数值插值,且无需之前的转换。计算的值将是具有相同参数数量的相同类型的变换函数。<matrix()> 有特殊规则。
两个变换函数 translate(0) 和 translate(100px) 类型相同,参数数量相同,因此可以直接进行数值插值。而 translateX(100px) 不是相同类型,且 translate(100px, 0) 的参数数量不同,因此这些变换函数不能在没有转换步骤的情况下插值。
具有相同原语的两种不同类型的变换函数,或具有不同参数数量的相同类型的变换函数可以进行插值。两种变换函数都需要先转换为共同的原语,然后再进行数值插值。计算的值将是具有插值后参数的原语。
以下示例描述了 translateX(100px) 到 translateY(100px) 的过渡效果,在悬停 div box 时触发,持续时间为3秒。两个变换函数都派生自相同的原语 translate(),因此可以插值。
div {
transform : translateX ( 100 px );
}
div:hover {
transform : translateY ( 100 px );
transition : transform 3 s ;
}
在过渡期间,两个变换函数将转换为共同的原语。translateX(100px) 被转换为 translate(100px, 0),而 translateY(100px) 被转换为 translate(0, 100px)。然后,两个转换后的变换函数可以进行数值插值。
如果两个变换函数在二维空间中共享一个原语,则两个变换函数将转换为二维原语。如果其中一个或两个变换函数是三维变换函数,则使用共同的三维原语。
在此示例中,二维变换函数被动画化为三维变换函数。共同的原语是 translate3d()。
div {
transform : translateX ( 100 px );
}
div:hover {
transform : translateZ ( 100 px );
transition : transform 3 s ;
}
首先,translateX(100px) 转换为 translate3d(100px, 0, 0),translateZ(100px) 转换为 translate3d(0, 0, 100px)。然后两个转换后的变换函数可以数值插值。
13. 矩阵的插值
在两个矩阵之间插值时,每个矩阵都分解为相应的平移、旋转、缩放、倾斜。每个分解的矩阵组件都会进行数值插值,并在最后一步重新组合回矩阵。
< style >
div {
transform : rotate( 45 deg );
}
div : hover {
transform : translate( 100 px , 100 px ) rotate( 1215 deg );
transition : transform 3 s ;
}
</ style >
< div ></ div >
源变换 rotate(45deg) 的变换函数数量与目标变换 translate(100px, 100px) rotate(1125deg) 的变换函数数量不同。根据 变换的插值 的最后一个规则,两个变换都必须通过矩阵插值。通过将变换函数转换为矩阵,关于三圈旋转的信息将丢失,元素只会旋转四分之一圈(90°)。
要实现上述示例中的三圈四分之一圈旋转,源和目标变换必须满足 变换的插值 的第三个规则。源变换可以是 translate(0, 0) rotate(45deg),以实现线性插值。
在接下来的部分中,我们区分了二维矩阵的插值和至少有一个不是 二维矩阵的矩阵之间的插值。
如果用于插值的矩阵之一是不可逆的,则所使用的动画功能必须根据相应动画规范的规则回退到离散动画。
13.1. 支持函数
以下小节中的伪代码使用了下列支持函数:
支持函数 (point 是 3 分量向量,matrix 是 4x4 矩阵,vector 是 4 分量向量): double determinant(matrix) 返回矩阵的 4x4 行列式 matrix inverse(matrix) 返回传入矩阵的逆矩阵 matrix transpose(matrix) 返回传入矩阵的转置矩阵 point multVecMatrix(point, matrix) 将传入的 point 与传入的 matrix 相乘并返回变换后的点 double length(point) 返回传入向量的长度 point normalize(point) 将传入的点的长度归一化为 1 double dot(point, point) 返回传入点的点积 double sqrt(double) 返回传入值的平方根 double max(double y, double x) 返回传入两个值中的较大值 double dot(vector, vector) 返回传入向量的点积 vector multVector(vector, vector) 将传入向量相乘 double sqrt(double) 返回传入值的平方根 double max(double y, double x) 返回传入两个值中的较大值 double min(double y, double x) 返回传入两个值中的较小值 double cos(double) 返回传入值的余弦值 double sin(double) 返回传入值的正弦值 double acos(double) 返回传入值的反余弦值 double abs(double) 返回传入值的绝对值 double rad2deg(double) 将弧度值转换为角度值并返回 double deg2rad(double) 将角度值转换为弧度值并返回 分解还使用了以下函数: point combine(point a, point b, double ascl, double bscl) result[0] = (ascl * a[0]) + (bscl * b[0]) result[1] = (ascl * a[1]) + (bscl * b[1]) result[2] = (ascl * a[2]) + (bscl * b[2]) return result
13.2. 二维矩阵的插值
13.2.1. 二维矩阵的分解
以下伪代码基于“Graphics Gems II”中的“unmatrix”方法,由 Jim Arvo 编辑。
伪代码中的矩阵使用列主序。矩阵条目的第一个索引表示列,第二个索引表示行。
输入: matrix ; 一个 4x4 矩阵 输出: translation ; 一个 2 分量向量 scale ; 一个 2 分量向量 angle ; 旋转角度 m11 ; 2x2 矩阵的 1,1 坐标 m12 ; 2x2 矩阵的 1,2 坐标 m21 ; 2x2 矩阵的 2,1 坐标 m22 ; 2x2 矩阵的 2,2 坐标 如果无法分解矩阵,则返回 false;如果可以分解,则返回 true double row0x = matrix[0][0] double row0y = matrix[0][1] double row1x = matrix[1][0] double row1y = matrix[1][1] translate[0] = matrix[3][0] translate[1] = matrix[3][1] scale[0] = sqrt(row0x * row0x + row0y * row0y) scale[1] = sqrt(row1x * row1x + row1y * row1y) // 如果行列式为负,则一个轴被翻转。 double determinant = row0x * row1y - row0y * row1x if (determinant < 0) // 翻转与最小单位向量点积的轴。 if (row0x < row1y) scale[0] = -scale[0] else scale[1] = -scale[1] // 重新归一化矩阵以去除缩放。 if (scale[0]) row0x *= 1 / scale[0] row0y *= 1 / scale[0] if (scale[1]) row1x *= 1 / scale[1] row1y *= 1 / scale[1] // 计算旋转角度并重新归一化矩阵。 angle = atan2(row0y, row0x); if (angle) // Rotate(-angle) = [cos(angle), sin(angle), -sin(angle), cos(angle)] // = [row0x, -row0y, row0y, row0x] // 感谢上面的归一化。 double sn = -row0y double cs = row0x double m11 = row0x double m12 = row0y double m21 = row1x double m22 = row1y row0x = cs * m11 + sn * m21 row0y = cs * m12 + sn * m22 row1x = -sn * m11 + cs * m21 row1y = -sn * m12 + cs * m22 m11 = row0x m12 = row0y m21 = row1x m22 = row1y // 将角度转换为度数,因为我们的旋转函数需要它。 angle = rad2deg(angle) return true
13.2.2. 分解二维矩阵值的插值
在两个分解的二维矩阵值可以插值之前,需要执行以下操作
输入: translationA ; 一个 2 分量向量 scaleA ; 一个 2 分量向量 angleA ; 旋转角度 m11A ; 2x2 矩阵的 1,1 坐标 m12A ; 2x2 矩阵的 1,2 坐标 m21A ; 2x2 矩阵的 2,1 坐标 m22A ; 2x2 矩阵的 2,2 坐标 translationB ; 一个 2 分量向量 scaleB ; 一个 2 分量向量 angleB ; 旋转角度 m11B ; 2x2 矩阵的 1,1 坐标 m12B ; 2x2 矩阵的 1,2 坐标 m21B ; 2x2 矩阵的 2,1 坐标 m22B ; 2x2 矩阵的 2,2 坐标 // 如果一个矩阵的 x 轴被翻转,另一个矩阵的 y 轴也被翻转, // 则转换为未翻转的旋转。 if ((scaleA[0] < 0 && scaleB[1] < 0) || (scaleA[1] < 0 && scaleB[0] < 0)) scaleA[0] = -scaleA[0] scaleA[1] = -scaleA[1] angleA += angleA < 0 ? 180 : -180 // 不要绕长方向旋转。 if (!angleA) angleA = 360 if (!angleB) angleB = 360 if (abs(angleA - angleB) > 180) if (angleA > angleB) angleA -= 360 else angleB -= 360
然后,源矩阵的分解值中的每个分量,包括 translation、scale、angle、m11 到 m22,将与目标矩阵中相应的分量进行线性插值。
13.2.3. 重构二维矩阵
插值完成后,结果值用于转换元素的用户空间。一种使用这些值的方法是将它们重构为 4x4 矩阵。可以按照下面的伪代码完成此操作。
伪代码中的矩阵使用列主序。矩阵条目的第一个索引表示列,第二个索引表示行。
输入: translation ; 一个 2 分量向量 scale ; 一个 2 分量向量 angle ; 旋转角度 m11 ; 2x2 矩阵的 1,1 坐标 m12 ; 2x2 矩阵的 1,2 坐标 m21 ; 2x2 矩阵的 2,1 坐标 m22 ; 2x2 矩阵的 2,2 坐标 输出: matrix ; 初始化为单位矩阵的 4x4 矩阵 matrix[0][0] = m11 matrix[0][1] = m12 matrix[1][0] = m21 matrix[1][1] = m22 // 平移矩阵。 matrix[3][0] = translate[0] * m11 + translate[1] * m21 matrix[3][1] = translate[0] * m12 + translate[1] * m22 // 旋转矩阵。 angle = deg2rad(angle); double cosAngle = cos(angle); double sinAngle = sin(angle); // 新的临时,初始化为单位矩阵的 4x4 矩阵 rotateMatrix rotateMatrix[0][0] = cosAngle rotateMatrix[0][1] = sinAngle rotateMatrix[1][0] = -sinAngle rotateMatrix[1][1] = cosAngle matrix = post-multiply(rotateMatrix, matrix) // 缩放矩阵。 matrix[0][0] *= scale[0] matrix[0][1] *= scale[0] matrix[1][0] *= scale[1] matrix[1][1] *= scale[1]
14. 变换函数的数学描述
在数学上,所有的变换函数都可以表示为以下形式的 4x4 变换矩阵:
矩阵中的一个平移单位相当于元素本地坐标系中的 1 像素。
-
一个具有六个参数 a, b, c, d, e 和 f 的二维 3x2 矩阵等价于以下矩阵:
-
一个具有参数 tx 和 ty 的二维平移等价于一个三维平移,其中 tz 的值为零。
-
一个具有参数 sx 和 sy 的二维缩放等价于一个三维缩放,其中 sz 的值为一。
-
一个具有参数 alpha 的二维旋转等价于一个具有向量 [0,0,1] 和参数 alpha 的三维旋转。
-
一个具有参数 alpha 和 beta 的二维倾斜变换等价于以下矩阵:
-
沿 X 轴的二维倾斜变换,参数为 alpha,等价于以下矩阵:
-
沿 Y 轴的二维倾斜变换,参数为 beta,等价于以下矩阵:
15. 隐私和安全考量
用户代理必须以攻击者无法推测信息并发起时间攻击的方式实现变换操作。
时间攻击是一种通过研究操作执行时间来获取受保护内容信息的方法。
目前没有关于该规范中特定的隐私或安全问题的报告。
变更
自2018年11月30日工作草案以来的变更
-
没有实质性更改
-
CR 的模板和样式更新
自2017年11月30日工作草案以来的变更
-
移除规范文本中将 patternTransform 和gradientTransform 表示为 transform 属性的部分,这将由 SVG 2 规范说明。
-
添加了 隐私和安全 部分。
-
为 transform、
gradientTransform
和patternTransform
属性添加了特殊语法。 -
澄清伪代码中矩阵条目的索引顺序。
-
澄清重新组合伪代码中的相乘顺序。
-
澄清 transform 在溢出区域的行为。
-
从中性元素列表中移除 translateX(0),translateY(0),scaleX(0) 和 scaleY(0)。
-
移除变换函数定义中对 3D 变换的任何引用。
-
指定匹配长度的 <transform-list> 之间的插值,避免两列表的公共前缀使用矩阵插值。
-
不对非替换的内联框、表格列框和表格列组框使用 transform。
-
为
pattern
,linearGradient
,radialGradient
和clipPath
元素定义了目标坐标空间。 -
从变换函数基元中移除 3 值 <rotate()>。
-
为绘制服务器和
clipPath
元素定义参考框。 -
指定当旋转属性值为 3 值时,过渡的起始或结束值的行为。
-
在 stroke-box 和 content-box 中添加 transform-box。在所有规范中统一框映射行为。
-
编辑改动。
致谢
编辑们要感谢 Robert O’Callahan, Cameron McCormack, Tab Atkins, Gérard Talbot, L. David Baron, Rik Cabanier, Brian Birtles, Benoit Jacob, Ken Shoemake, Alan Gresley, Maciej Stochowiak, Sylvain Galineau, Rafal Pietrak, Shane Stephens, Matt Rakow, XiangHongAi, Fabio M. Costa, Nivesh Rajbhandari, Rebecca Hauck, Gregg Tavares, Graham Clift, Erik Dahlström, Alexander Zolotov, Amelia Bellamy-Royds 和 Boris Zbarsky 对文档的仔细审阅、评论和修改意见。