首先聲明一點,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、官方文檔