CSS 3学习——transform 2D转换
首先声明一点,transform属性不为none的元素是它的定位子元素(绝对定位和固定定位)的包含块,而且对内创建一个新的层叠上下文。
注意:可以通过 transform-box 属性指定元素的那个盒子发生了变换,该属性的默认值是“border-box”,查MDN只有Firefox支持该属性(我试的没效果)。
CSS 3 中2D转换的实现用到两个属性:
属性 | 描述 | CSS |
---|---|---|
transform | 向元素应用 2D 或 3D 转换。 | 3 |
transform-origin | 指定变换的基点的位置。 | 3 |
transform-origin
该属性实际上是指定局部坐标系的原点位置。
语法:
transform-origin: [ left | center | right | top | bottom | <percentage> | <length> ]
|
[ left | center | right | <percentage> | <length> ]
[ top | center | bottom | <percentage> | <length> ] <length>?
|
[ center | [ left | right ] ] && [ center | [ top | bottom ] ] <length>?
默认值为: 50% 50% 0;
不可继承。
值的计算都是以元素的border box为基准的,transform-origin的默认值把局部坐标系的原点移到了元素的中心。
注意:关于转换元素创建的局部坐标系,参见规范中的解释:local coordinate system
如果只指定了一个值,那么第二个值作为 "center" 处理;如果只指定了一个或两个值,第三个值作为 “0px”处理。
如果指定了两个或更多的值(三个)而且没有使用关键字类型的值,或者只使用了关键字“center”,那么第一个值代表距离border box左边界的距离,第二个值代表距离border box上边界的距离,第三个值代表在 z 轴方向上的位置(或位移),而且必须为长度值<length>。
关键字 | 解释 |
top | 相当于垂直方向上的 0% |
right | 相当于水平方向上的 100% |
bottom | 相当于垂直方向上的 100% |
left | 相当于水平方向上的 0% |
center |
在垂直方向上相当于50% 或者在水平方向上相当于50% |
MDN上对于可取值举的例子:
1 /* 单值语法 */ 2 transform-origin: 2px; 3 transform-origin: bottom; 4 5 /* 双值语法 */ 6 /* 用两个数字值先水平后垂直,用一个数值一关键字或两关键字不强求顺序 */ 7 transform-origin: 3cm 2px; /* x-offset y-offset */ 8 transform-origin: 2px left; /* y-offset x-offset-keyword */ 9 transform-origin: left 2px; /* x-offset-keyword y-offset */ 10 transform-origin: right top; /* x-offset-keyword y-offset-keyword */ 11 transform-origin: top right; /* y-offset-keyword x-offset-keyword */ 12 13 /* 三值语法 */ 14 transform-origin: 2px 30% 10px; /* x-offset y-offset z-offset */ 15 transform-origin: 2px left 10px; /* y-offset x-offset-keyword z-offset */ 16 transform-origin: left 5px -3px; /* x-offset-keyword y-offset z-offset */ 17 transform-origin: right bottom 2cm; /* x-offset-keyword y-offset-keyword z-offset */ 18 transform-origin: bottom right 2cm; /* y-offset-keyword x-offset-keyword z-offset */
transform 2D转换
注意:
- 当对一个元素应用变换(2D 和 3D)时,连带着他的后代元素也应用了相同的变换。
- 应用了变换(2D 和 3D)的元素不会影响他周围元素的布局,其表现形式就像相对定位(position: relative)的元素的表现形式一样。也就是说,如果变换元素经过变换之后溢出了其父元素的边界,而其父元素的 overflow 属性又恰好是 scroll 或 auto,那么他的父元素就会产生滚动条,以便查看变换元素的溢出部分。
例子:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <style> 5 .div1 6 { 7 position: relative; 8 height: 200px; 9 width: 200px; 10 margin: 100px; 11 padding:10px; 12 border: 1px solid black; 13 overflow: auto; 14 } 15 16 .div2 17 { 18 padding:50px; 19 border: 1px solid black; 20 background-color: red; 21 transform: translate(100px) rotate(60deg) translate(100px,100px); 22 } 23 </style> 24 </head> 25 <body> 26 <div class="div1"> 27 <div class="div2">HELLO 28 </div> 29 </div> 30 </body> 31 </html>)
效果:
transform属性用于2D转换的方法函数主要有以下几个。
转换方法 | 转换方式 |
translate() | 平移 |
scale() | 缩放 |
rotate() | 旋转 |
skew() | 倾斜 |
matrix() | 矩阵变换 |
translate()
translate()函数指定元素的平移转换。他可接受两个参数,第一个参数表示沿 x 轴的平移量,第二个值表示沿 y 轴的平移量。如果只指定一个值,第二个值作为 0 处理。
另外还有只向一个方向平移的函数:
- translateX() 该函数只接受一个参数,表示沿 x 轴的平移量。
- translateY() 该函数只接受一个参数,表示沿 y 轴的平移量。
示例:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <style> 5 #div3{ 6 background-color: pink; 7 height: 250px; 8 width: 250px; 9 } 10 #div4{ 11 height: 100px; 12 width: 100px; 13 background-color: gray; 14 transform: translate(100px,100px); 15 } 16 #div5{ 17 height: 50px; 18 width: 50px; 19 background-color: yellow; 20 } 21 </style> 22 </head> 23 24 <body> 25 <div id="div3"> 26 <div id="div4" > 27 <div id="div5"></div> 28 </div> 29 </div> 30 </body> 31 </html>
效果:
从图中可以看出,作为子元素的黄色 div 也跟着平移了。
scale()
scale() 函数可以接受两个数字参数,表示缩放倍数。
第一个参数表示沿 x 轴的缩放,第二个值表示沿 y 轴缩放。小于 1,缩小;等于 1,大小不变;大于 1,放大。
如果只指定一个值,那么第二个值会被认为等于第一个值。
同样也有只沿一个方向缩放的函数:
- scaleX() 该函数只接受一个参数,表示沿 x 轴缩放。相当于scale(tx, 1)。
- scaleY() 该函数只接受一个参数,表示沿 y 轴缩放。相当于scale(1, ty)。
示例:
1 <style> 2 #div3{ 3 background-color: pink; 4 height: 200px; 5 width: 200px; 6 } 7 #div4{ 8 height: 100px; 9 width: 100px; 10 background-color: gray; 11 transform: translate(50px,50px) scale(2); 12 } 13 #div5{ 14 height: 50px; 15 width: 50px; 16 background-color: yellow; 17 } 18 </style> 19 20 <body> 21 <div id="div3"> 22 <div id="div4" > 23 <div id="div5"></div> 24 </div> 25 </div> 26 </body>
效果:
沿 x 轴和沿 y 轴的缩放距离是如何确定的呢?
具体要看基点在哪里,以上面的示例为例说明。
示例中粉色 div 是边长200px的正方形,灰色 div 是边长为100px的正方形,黄色 div 是边长50px的正方形,发生变换的是灰色 div。
灰色 div 变换的基点默认情况下在他的中心点。
以变换基点作局部坐标系,坐标轴的方向:x 轴向右为正;y 轴向下为正。此时在局部坐标系中,灰色 div 的边长被坐标原点均分到坐标轴的正负轴上,即正轴上的长度与负轴上的长度的比例为 1:1。当元素缩放变换时,就用局部坐标系中正负半轴上的部分边长分别乘以对应方向上的缩放倍数,用得到的值减去原来分布在半轴上的边长就得到元素应该在改半轴上扩展的距离。
示例中,灰色 div 分布在局部坐标系 x 轴正半轴上的边长为50px,那么50*2 = 100,100 - 50 = 50,所以灰色 div 沿局部坐标系 x 轴的正轴方向向外扩展50px,分布在其他半轴上的边长同理。最终结果就是灰色 div 恰好完全覆盖住粉色 div。
下面再举一个例子。
图中粉色 div 不考虑黑色边框时是边长为300px的正方形,灰色 div 是边长100px的正方形。如果灰色 div 的初始位置如图中所示,距离粉色 div 左侧和顶部分别为50px,那么灰色 div 仅仅通过缩放变换(需要改变变换基点的位置),要放大几倍能刚好覆盖粉色 div(不覆盖边框)?
这个问题可以通过前文介绍的缩放距离的计算方法计算出来,我的计算结果:
- 先把变换基点设置为 transform-origin: 25px 25px;
- 再用scale()函数放大 3 倍
代码:
1 <style> 2 #div3{ 3 position:relative; 4 background-color: pink; 5 height: 300px; 6 width: 300px; 7 border: 1px solid black; 8 } 9 #div4{ 10 position: absolute; 11 top: 50px; 12 left: 50px; 13 height: 100px; 14 width: 100px; 15 background-color: gray; 16 transform-origin: 25px 25px; 17 transform: scale(3); 18 } 19 #div5{ 20 height: 50px; 21 width: 50px; 22 background-color: yellow; 23 } 24 </style> 25 26 <body> 27 <div id="div3"> 28 <div id="div4" > 29 <div id="div5"></div> 30 </div> 31 </div> 32 </body>
效果:
另外,scale()函数还可以接受负值。
比如:
如果是scale(-1, 1),则转换后元素的位置与元素经scale(1, 1)变换后的位置关于局部坐标系的 y 轴的对称。
如果是scale(1, -1),则转换后元素的位置与元素经scale(1, 1)变换后的位置关于局部坐标系的 x 轴的对称。
如果是scale(-1, -1),则转换后元素的位置与元素经scale(1, 1)变换后的位置关于局部坐标系的原点(变换基点)中心对称。
示例:
代码还是前一例的代码。
scale(-3)变换与scale(3)变换的位置关于变换基点(25px, 25px)中心对称。
rotate()
rotate()函数通过指定的角度参数让元素进行2D旋转变换。
接受一个角度值,用来指定旋转的幅度。
如果这个值为正值,元素顺时针旋转;如果这个值为负值,元素逆时针旋转。
rotate()变换的元素绕着什么旋转呢?
其实是绕着局部坐标系(原点在变换基点)的 z 轴旋转。
示例:
1 <style> 2 #div3{ 3 position:relative; 4 background-color: pink; 5 height: 300px; 6 width: 300px; 7 border: 1px solid black; 8 margin: auto; 9 } 10 #div4{ 11 position: absolute; 12 top: 50px; 13 left: 50px; 14 height: 100px; 15 width: 100px; 16 background-color: gray; 17 transform-origin: 25px 25px; 18 transform: rotate(-90deg); 19 } 20 #div5{ 21 height: 50px; 22 width: 50px; 23 background-color: yellow; 24 } 25 </style> 26 27 <body> 28 <div id="div3"> 29 <div id="div4" > 30 <div id="div5"></div> 31 </div> 32 </div> 33 </body>
效果:
skew()
通过skew()函数,元素可以实现倾斜变换。
该函数可以接受两个参数(可以为负值),第一个参数表示元素的border box 的垂直边与参照坐标系 y 轴的夹角,第二个参数表示元素的border box 的水平边与参照坐标系 x 轴的夹角。
如果只指定了一个值,第二个值作为 0 处理。
另外也有只能指定一个夹角的函数:
- skewX() 只接受一个参数,相当于skew()函数的第一个参数。
- skewY() 只接受一个参数,相当于skew()函数的第二个参数。
示例:
1 <style> 2 3 /***********skew************/ 4 #div6{ 5 position:relative; 6 height: 300px; 7 width: 300px; 8 border: 1px solid black; 9 margin: auto; 10 } 11 #div7{ 12 position:absolute; 13 top:75px; 14 left:75px; 15 height: 150px; 16 width: 150px; 17 background-color: #5555FF; 18 } 19 #div8{ 20 position:absolute; 21 top:75px; 22 left:75px; 23 height: 150px; 24 width: 150px; 25 opacity: 0.7; 26 background-color: #66FF66; 27 } 28 </style> 29 30 <body> 31 <!--skew--> 32 <div id="div6"> 33 <div id="div7"></div> 34 <div id="div8"></div> 35 </div> 36 </body>
上面的代码的效果如下:
给 id="div8" 的元素添加以下CSS代码:
transform: skew(60deg);
或者
transform: skew(-120deg);
可以得到下图所示效果:
图中标出了局部坐标系及夹角。
matrix()
矩阵变换。
利用矩阵变换可以实现以上介绍的所有2D变换。也就是说,matrix()函数是以上几个函数的并集。
该函数可以接受6个参数,分别用a、b、c、d、e、f指代:
matrix(a, b, c, d, e, f);
这6个参数组成下面这个矩阵:
这就是 transform 的2D变换矩阵。
矩阵变换的原理:
x、y表示变换前元素上某一点在局部坐标系中的横纵坐标。
令
x' = ax + cy + e
y' = bx + dy + f
则 x'、y' 表示变换后元素上这一点在局部坐标系中的横纵坐标。
根据 x' 和 y' 的计算公式,只要给a、b、c、d、e、f指定不同的值就可以实现上述几种变换。
用matrix()实现平移变换
平移变换就是给原坐标分别加上我们指定的值。
举例,如果变换前元素中有一个点的坐标为(10px, 20px),经过 translate(20px, 5px) 的平移变换,则变换后该点的横坐标变为
10px + 20px = 30px
纵坐标变为
20px + 5px = 25px
即点(10px, 20px)平移到了点 (30px, 25px)。
根据 x' 的计算公式,让 a = 1,c = 0,e = 20,则 x' = 30;
根据 y' 的计算公式,让 b = 0,d = 1,f = 5,则 y' = 25;
所以 matrix(1, 0, 0, 1, 20, 5) 可以实现平移变换 translate(20px, 5px)。
其实,不管是什么样的平移变换,只要让 a = 1, b = 0,c = 0,d = 1,那么 e 就相当于translate()的第一个参数,f 就相当于translate()的第二个参数。
用matrix()实现缩放变换
缩放变换变换前的横纵坐标分别乘以指定的缩放倍数,从而得到变换后的横纵坐标。
举例,用matrix()实现scale(2,3)。
x' = 2x
y' = 3y
只要令 a = 2,c = 0,e = 0;b = 0,d = 3,f = 0 即可。即:
matrix(2, 0, 0, 3, 0, 0);
其实,不管是什么样的缩放变换,只要让 b = 0,c = 0,e = 0,f = 0,那么 a 就相当于scale()的第一个参数,d 就相当于scale()的第二个参数。
用matrix()实现旋转变换
如果有旋转变换rotate(θ),则 x' 、y' 的计算公式就变为:
x’ = x*cosθ - y*sinθ + 0 = x*cosθ - y*sinθ
y’ = x*sinθ + y*cosθ + 0 = x*sinθ + y*cosθ
也就是说
a = cosθ,c = - sinθ , e = 0
b = sinθ,d = cosθ,f = 0
即
matrix(cosθ, sinθ, -sinθ, cosθ, 0, 0);
用matrix()实现倾斜变换
如果有倾斜变换scale(θx, θy),则 x' 、y' 的计算公式就变为:
x’ = x + y*tanθx + 0 = x + y*tanθx
y’ = x*tanθy + y + 0 = x*tanθy + y
也就是说
a = 1,c = tanθx , e = 0
b = tanθy,d = 1,f = 0
即
matrix(1, tanθy, tanθx, 1, 0, 0);
用matrix()实现简单镜像变换
这里说的简单镜像变换是指变换前的元素和变换后的元素关于经过局部坐标系原点的直线 y = kx 对称。
盗一张张大神的图:
关于参数的推到直接看张大神的文章,这里直接贴结果。
x' = (1-k*k) / (k*k+1) *x + 2k / (k*k+1) *y;
y' = 2k / (k*k+1) *x + (k*k-1) / (k*k+1) *y;
所以
a = (1-k*k) / (k*k+1)
b = 2k/(k*k+1)
c = 2k/(k*k+1)
d = (k*k-1)/(k*k+1)
e = 0
f = 0
(完)
参考资料
1、官方文档