byzhangxinxufromhttp://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=4965
一、HTML transform和SVG transform
SVG中自帶transform屬性,沒錯(cuò),是屬性,例如:
普通的HTML元素沒有transform屬性,但是支持CSS3的transform, 好奇的小伙伴可能會(huì)疑問了,CSS3中的transform變換,跟SVG中的transform是什么關(guān)系呢?
恩,有點(diǎn)類似于謝霆鋒和陳冠希之間的關(guān)系,有些小復(fù)雜。

OK, 先說說相似之處吧。
一些基本的變換類型是一樣的,包括:位移translate, 旋轉(zhuǎn)rotate, 縮放scale, 斜切skew以及直接矩陣matrix. 但只局限于2D層面的變換。SVG似乎只支持二維變換(若有不對,歡迎指正),且類似translateX,rotateX也都是不支持的。
下面就是不一樣的地方了:
1.CSS3transform一般用在普通元素上,雖然也可以應(yīng)用在SVG元素上,但是IE瀏覽器(IE edge未測試)卻不支持SVG元素;
rect {/* IE說:你這是弄啥來? */transform:rotate(45deg);}
2.HTML元素的CSS3 transform和SVG的transform坐標(biāo)系統(tǒng)大相徑庭;
平常我們使用transform其坐標(biāo)是相對于當(dāng)前元素而言的,默認(rèn)是元素的中心點(diǎn)變換,我們可以通過transform-origin屬性改變變換的中心點(diǎn)。而SVG中的transform的坐標(biāo)變換的是相對于畫布的左上角計(jì)算的,跟HTML的transform差別較大,理解上也更加麻煩。而本文就是徹底理清SVG中的transform到底是怎么工作的。
3.具體的語法細(xì)節(jié)有差異。SVGtransform屬性語法有些自帶偏移。而CSStransform則更加純粹些。
//zxx: 據(jù)說CSS的transform和SVG的transform屬性即將合并。
二、SVG transform translate位移
我們先來看下最簡單最基本的translate位移變換,例如,我們偏移(295,115)大小的位置,HTML元素的偏移(下圖左)和SVG元素的偏移(下圖右)就會(huì)不一樣。一個(gè)是相對自己的中心點(diǎn)(下圖左),一個(gè)是SVG的左上角(下圖右)。

雖然兩者的相對位置不一樣,但是,對于單純地位移來講,無論你相對于那個(gè)點(diǎn)位置,實(shí)際偏移的位置都是一樣的,因此,從表現(xiàn)上講,兩者最終的位置看上去還是一樣的。
您可以狠狠地點(diǎn)擊這里:HTML translate和SVG translate比對demo

前面我們提到過,SVG元素也能使用CSS3的transform進(jìn)行變換(非IE瀏覽器),但是只能支持2D層面的幾個(gè)屬性,例如translateX(tx),translateY(ty)以及translate(tx[, ty]).translateZ(tz)則并不支持。
如果我們使用SVG元素自帶的transform屬性進(jìn)行變換,則僅支持translate(tx[ ty])這種用法(缺省使用0代替),當(dāng)多個(gè)參數(shù)值的時(shí)候,可以使用逗號(hào),或者直接空格分隔,但是不能包含單位,例如下面這種寫法直接翹辮子:
transform="translate(30px 12px)"

下面這種無單位寫法才可以:
transform="translate(30 12)"
transform="translate(30, 12)"


另外,和CSS3的transform一樣,SVG中的translate位移也是支持多聲明累加的。例如:
transform="translate(30 12) translate(30 12)"
等同于:
transform="translate(60 24)"
需要注意的是,倆個(gè)translate中間不要混有其他的transform變換。否則,最終的位移就不是簡單的相加了。
三、SVG transform rotate旋轉(zhuǎn)
上面的位移變換,我們似乎沒看到明顯的不一樣。但是,從這里的旋轉(zhuǎn)變換開始,就可以看出明顯的差異了。
下面圖示的是基本的45度旋轉(zhuǎn)(來自css-tricks)(左HTML元素,右SVG元素):

由于SVG元素的默認(rèn)是SVG左上角為中心變換的,因此,矩形旋轉(zhuǎn)的幅度就有了過山車的感覺。
您可以狠狠地點(diǎn)擊這里:HTML rotate和SVG rotate比對demo
結(jié)果會(huì)發(fā)現(xiàn),兩者位置大相徑庭了:

CSStransform中的rotate語法比較直白:rotate(angle),就一個(gè)angle參數(shù),表示角度大小,不過必須要有單位,比如deg(度), turn(圈), grad(百分度 – 一種角的測量單位,定義為一個(gè)圓周角的1/400。常用于建筑或土木工程的角度測量),甚至可以使用calc()計(jì)算,例如:calc(.25turn - 30deg).
但是,SVG中的屬性transform旋轉(zhuǎn)就沒有這么多花頭,單位?哦,別逗了,毛線都沒有,直接光禿禿的數(shù)值,表示角度deg,例如:
transform="rotate(45)"
具體語法為:rotate(angle[ x y]). 大家注意到?jīng)]有,這里有個(gè)[ x y],[]表示這個(gè)可選參數(shù)。也就是說,SVG中的rotate旋轉(zhuǎn)比CSS的rotate多了一個(gè)可選參數(shù),那這個(gè)可選參數(shù)[ x y]表示什么意思呢?
告訴你,是非常有用的東西。用來偏移transform變換中心點(diǎn)的。
為什么說非常有用呢?SVG元素默認(rèn)是基于左上角的,但是我們的旋轉(zhuǎn)元素往往都在SVG的中間區(qū)域,此時(shí)旋轉(zhuǎn)跨度太大,智商余額不足的我們就腦補(bǔ)不過來,此時(shí)難免希望可以像CSS的transform變換一樣,圍繞元素的中心點(diǎn)變換。怎么辦?
我們可以借助CSS3transform-origin修改SVG元素默認(rèn)的變換中心點(diǎn),然后配合CSS變換。但是,我們前面多次提到,IE瀏覽器的SVG元素不識(shí)別CSS中的transform. 所以,從兼容性上講,CSS策略是不可行的。難道就沒有其他辦法了,有,就是這里的可選參數(shù)[ x y],通過對變換中心點(diǎn)的偏移修正,我們也能讓SVG元素圍繞自身的中心點(diǎn)旋轉(zhuǎn)了。
所以,上面的demo,我們稍微修改下,就能讓矩形圍繞自己旋轉(zhuǎn)了,見下:
您可以狠狠地點(diǎn)擊這里:SVG元素也圍繞自身中心點(diǎn)旋轉(zhuǎn)demo

使用原理圖表示就是下面這樣(左HTML旋轉(zhuǎn),右SVG元素偏移旋轉(zhuǎn)):

同樣的,rotate旋轉(zhuǎn)可以多個(gè)值并存,例如下面的CSS和attribute用法:
transform: rotate(45deg) rotate(-45deg);
transform="rotate(45) rotate(-45)"
然而,需要注意的是,SVG屬性的transform聲明的中心變換坐標(biāo)是不能共享的。
因此,雖然transform="rotate(45, 90 75)"是中心點(diǎn)旋轉(zhuǎn),但是,后面再添加其他東西,例如:rotate(-45)則偏移值忽略,終中心點(diǎn)還是SVG的左上角(0,0)位置,好慘!
例如原來的45度旋轉(zhuǎn),再加個(gè)-45度反向旋轉(zhuǎn):
結(jié)果位置回去了?才怪呢!

CSS的是又回去了,但是SVG的確是掛在月球上了。究其原因是rotate(-45)又是相對SVG左上角變換的啦!

您可以狠狠地點(diǎn)擊這里:SVG連續(xù)旋轉(zhuǎn)demo
雖然乍看上去,好像SVG的坐標(biāo)系統(tǒng)有些怪怪的,但是,實(shí)際上,在有些需求場景下,SVG這種看似獨(dú)立的偏移系統(tǒng)更容易實(shí)現(xiàn)一些功能。
比方說,我們希望某個(gè)SVG元素先以右下角為中心旋轉(zhuǎn)90度,然后再以右上角為中心旋轉(zhuǎn)90度,該怎么處理?
對于SVGtransform,我們直接面向需求寫數(shù)值就可以了。假設(shè)我們的SVG元素的高寬是120*90, 左上角坐標(biāo)是(30,30), 則,顯然,右下角坐標(biāo)是(150,120), 右上角坐標(biāo)是(150,30),于是,我們的transform值就很簡單:
transform="rotate(90, 150 120) rotate(90 150 30)"
參見下面的示意圖(示意圖的坐標(biāo)與上面略有出入,但不影響原理示意):

但是,如果我們使用之前容易理解的CSS3來實(shí)現(xiàn),反而就復(fù)雜了,因?yàn)镃SS3中的transform的變換點(diǎn)只能一次性指定,如果要實(shí)現(xiàn)不同變換點(diǎn)的旋轉(zhuǎn)效果,只能通過translate再次偏移,例如,實(shí)現(xiàn)上面的右下角再右上角90度旋轉(zhuǎn),則要這樣:
transform-origin: right bottom;/* 或者 100% 100% */transform:? rotate(90deg)? translate(0, -100%)/* 從右下到右上 */rotate(90deg)? translate(0, 100%);
Gif示意下就是:

顯然要麻煩很多??梢?,兩種坐標(biāo)系統(tǒng)沒有絕對的優(yōu)劣。
您可以狠狠地點(diǎn)擊這里:右下再右上旋轉(zhuǎn)90度demo
上圖為兩種變換的最終效果,雖然最終的效果是一樣的,但是,從理解上而言,這回,是SVG的transform反而更容易理解。還是那句話,辯證看問題,凡事無絕對。
四、SVG transform scale縮放
SVG中的縮放的語法就比較單純了,就scale(sx[, sy]),sx表示橫坐標(biāo)縮放比例,sy表示縱坐標(biāo)縮放比例。其中sy是可缺省的,如果缺失,表示使用和sx一樣的值,也就是等比例縮放。其中,sx和sy兩個(gè)參數(shù)可以逗號(hào)分隔,也可以使用空格分隔。這和CSS3中的使用有所不同,兩外,SVGtransform屬性值縮放是不支持scaleX,scaleY這些鬼的。
同樣的,CSS控制的transform和SVG元素屬性控制的transform的坐標(biāo)系統(tǒng)是不一樣的。一個(gè)默認(rèn)元素中心(下圖左),一個(gè)是SVG畫布左上角(下圖右),于是(from css-tricks):

因此,當(dāng)我們對SVG元素scale縮放時(shí)候,發(fā)現(xiàn)位置也有出乎我們意料,就應(yīng)該知道是怎么回事了。
rotate旋轉(zhuǎn)雖然也是迥異坐標(biāo),但是其參數(shù)自帶偏移參數(shù),我們我們移個(gè)花接個(gè)木,還是可以得到我們想要的結(jié)果。但是,scale縮放這里,就要悲慘很多了,沒有自帶偏移參數(shù),于是,當(dāng)我們要實(shí)現(xiàn)SVG元素居中縮放的效果,還需要使用translate手動(dòng)偏移。
怎么個(gè)手動(dòng)偏移法呢?即使先translate其中心點(diǎn)位置到元素的中心坐標(biāo)位置,然后縮放,然后坐標(biāo)再反方向還原回去。例如,某元素中心點(diǎn)坐標(biāo)是(95, 75), 垂直縮放1.5倍的效果則是:
transform="translate(95 75) scale(1, 1.5) translate(-95 -75)"
您可以狠狠地點(diǎn)擊這里:CSS transform和SVG transform scale縮放demo
對應(yīng)的CSS代碼就簡單多了,直接:
.scale {
transform: scale(1, 1.5);
}
然后最終效果都是一樣的:

使用Gif原理示意如下:

五、SVG transform skew斜切
先來了解下CSS中的斜切,斜切,如果單純切一個(gè)方向,我們可以看成把矩形變成了平行四邊形,其總面積不變化。
以純X軸變換舉例,skewX(-45deg)則下面這樣子(灰色方塊為原始位置):

skewX(45deg)則下面這樣子:

對于SVG的skew斜切變換,表現(xiàn)效果原理是一樣的。但是,使用的語法卻相當(dāng)有意思。
在前面的一些變換中,例如位移、縮放之類是不支持translateX,scaleX這種CSS常見用法的,但是這里的skew卻有點(diǎn)讓人哭笑不得:不支持skew(x[, y])這種語法,而只能是skewX或者skewY.
別問我為什么?我只是大自然的搬運(yùn)工,我不生產(chǎn)語法。
因此,沒有:
transform="skew(45, 0)"
只有:
transform="skewX(45)"
同樣的,由于變換中心點(diǎn)的差異,CSS實(shí)現(xiàn)的變換和SVG屬性變換往往最后的位置是不一樣的:

不僅如此,如果元素的中心點(diǎn)不是就是SVG的左上角,則skewX(α1) skewX(α2)的最終位置和skewX(α1 + α2)是不一樣的(位移和旋轉(zhuǎn)不會(huì)這樣子)。
您可以狠狠地點(diǎn)擊這里:CSS SVG transform skew斜切差異及連續(xù)斜切差異demo
正如demo所示,CSS的和SVG的位置差異很大:

SVG的連續(xù)變換和一次性變換的位置也是不一樣的:

可能有人要疑問,為何連續(xù)斜切變換和一次性變換位置會(huì)不一樣?其實(shí)原因很簡單,因?yàn)樾鼻械慕嵌群驮仄拼笮〔⒉皇蔷€性的,比方說,從70到80度和80度到90度之間的位移大?。m然都是10度的變化區(qū)間)是顯然不是一個(gè)檔次的。因此,分開多次連續(xù)斜切最終的坐標(biāo)偏移要比一次性偏移來得小。
最后,和縮放一樣,你想要讓SVG元素中心點(diǎn)斜切,可以先translate偏移,在skew變換。就不重復(fù)舉例演示了。
六、其他居中變換處理
像縮放、斜切這些SVG變換,想要如CSStransform-origin:50% 50%一樣的中心點(diǎn)變換效果,需要事先位移,我們有沒有其他辦法呢?
這里有兩個(gè)思路可供大家參考下。
1. 原始中心位置乃SVG左上角
拿45度旋轉(zhuǎn)舉例,我們可以把元素默認(rèn)就放在中心點(diǎn)和SVG左上角重合的位置上,參見下面的gif演示:

于是,我們原來的3步曲就變成了2步曲:
translate(140 105) rotate(45) translate(-140 -105) → translate(140 105) rotate(45)
2. 通過viewBox調(diào)整
viewBox可以用來改變SVG畫布的視區(qū),這個(gè)我之前專門撰文介紹過,是SVG學(xué)習(xí)必備被深入理解的基礎(chǔ)知識(shí),參見:“理解SVG的viewport,viewBox,preserveAspectRatio”一文。
我們可以把元素默認(rèn)掛在左上角,然后,通過viewBox做手腳,讓元素呈現(xiàn)的位置并不是真正的左上角,例如應(yīng)用viewBox='-140 -105 280 210',則變化如下示意圖:

此時(shí),我們只需要讓元素旋轉(zhuǎn)就可以了,無需額外的做translate位移,見下gif:

七、結(jié)束語
本文介紹的內(nèi)容實(shí)際上都還是非?;镜?。實(shí)際SVG應(yīng)用的時(shí)候,可能是多個(gè)變換參雜在一起,所以,如果本文介紹的幾個(gè)基本變換都沒搞清楚,到時(shí)候,想必是想破腦袋都不明白怎么元素跑這里了,怎么變成這樣了!
本文的這些知識(shí)點(diǎn)雖然基本,但是相當(dāng)重要的。再加上,不同的變換方法的語法細(xì)節(jié)還不一樣。有的自帶偏移,有的需要手動(dòng)偏移等等;不同變換的前后位置不同,甚至同一變換分開連續(xù)變換和一次性變換的結(jié)果都不一樣等等;都要求大家要細(xì)心耐心閱讀。
本文內(nèi)容和結(jié)構(gòu)參考自:Transforms on SVG Elements. 但要比原文要精煉很多,同時(shí),每一個(gè)變換都有親自實(shí)踐認(rèn)證,因此,從品質(zhì)上講,可能還要略高一籌。
對了,矩陣matrix沒有細(xì)說過,這個(gè)可以參考我之前的文章:“理解CSS3 transform中的Matrix(矩陣)”,一脈相承的。