理解SVG座標系統和變換: transform屬性

發表於2015-09-23

SVG元素可以通過縮放,移動,傾斜和旋轉來變換-類似HTML元素使用CSS transform來變換。然而,當涉及到座標系時這些變換所產生的影響必然有一定差別。在這篇文章中我們討論SVG的transform屬性和CSS屬性,包括如何使用,以及你必須知道的關於SVG座標系變換的知識。

這是我寫的SVG座標系統和變換部分的第二篇。在第一篇中,包括了任何要理解SVG座標系統基礎的需要知道的內容;更具體的是, SVG viewport, viewBox 和 preserveAspectRatio 屬性。

這一部分我建議你先閱讀第一篇,如果沒有,確保你在閱讀這篇之前已經讀了第一篇。

transform屬性值

tranform屬性用來對一個元素宣告一個或多個變換。它輸入一個帶有順序的變換定義列表的<transform-list>值。每個變換定義由空格或逗號隔開。給元素新增變換看起來如下:

有效地SVG變換有:旋轉縮放移動, 和傾斜transform屬性中使用的變換函式類似於CSS中transform屬性使用的CSS變換函式,除了引數不同。

注意下列的函式語法定義只在transform屬性中有效。檢視section about transforming SVGs with CSS properties獲取關於CSS變換屬性中使用的語法資訊。

Matrix

你可以使用matrix()函式在SVG元素上新增一個或多個變換。matrix變換語法如下:

上述宣告通過一個有6個值的變換矩陣宣告一個變換。matrix(a,b,c,d,e,f)等同於新增變換matrix[a b c d e f]

如果你不精通數學,最好不要用這個函式。對於那些精通的人,你可以在這裡閱讀更多關於數學的內容。因此這個函式很少使用-我將忽略來討論其他變換函式。

Translation

要移動SVG元素,你可以用translate()函式。translate函式的語法如下:

translate()函式輸入一個或兩個值得來宣告水平和豎直移動值。tx代表x軸上的translation值;ty代表y軸上的translation值。

ty值是可選的,如果省略,預設值為0txty值可以通過空格或者逗號分隔,它們在函式中不代表任何單位-它們預設等於當前使用者座標系單位。

下面的例子把一個元素向右移動100個使用者單位,向下移動300個使用者單位。

上述程式碼如果以translate(100, 300)用逗號來分隔值的形式宣告一樣有效。

Scaling

你可以通過使用scale()函式變換來向上或者向下縮放來改變SVG元素的尺寸。scale變換的語法是:

scale()函式輸入一個或兩個值來宣告水平和豎直縮放值。sx代表沿x軸的縮放值,用來水平延長或者拉伸元素;sy代表沿y軸縮放值,用來垂直延長或者縮放元素。

sy值是可選的,如果省略預設值等於sxsxsy可以用空格或者逗號分隔,它們是無單位值。

下面例子把一個元素的尺寸根據最初的尺寸放大兩倍:

下列例子把一個元素縮放到最初寬度的兩倍,並且把高度壓縮到最初的一半:

上述例子使用逗號分隔的值例如scale(2, .5)仍然有效。

這裡需要注意當SVG元素縮放時,整個座標系被縮放,導致元素在視窗中重新定位,現在不用擔心這些,我們會在下一節中討論細節。

Skew

SVG元素也可以被傾斜,要傾斜一個元素,你可以使用一個或多個傾斜函式skewX 和 skewY

函式skewX宣告一個沿x軸的傾斜;函式skewY宣告一個沿y軸的傾斜。

傾斜角度宣告是無單位角度的預設是度。

注意傾斜一個元素可能會導致元素在視窗中重新定位。在下一節中有更多細節。

Rotation

你可以使用rotate()函式來旋轉SVG元素。這個函式的語法如下:

rotate()函式對於給定的點和 旋轉角度值執行旋轉。不像CSS3中的旋轉變換,不能宣告除degress之外的單位。角度值預設無單位,預設單位是度。

可選的cxcy值代表無單位的旋轉中心點。如果沒有設定cxcy,旋轉點是當前使用者座標系的原點(檢視第一部分如果你不知道使用者座標系是什麼。)

在函式rotate()中宣告旋轉中心點一個快捷方式類似於CSS中設定transform: rotate()transform-origin。SVG中預設的旋轉中心是當前使用的使用者座標系的左上角,這樣也許你無法建立想要的旋轉效果,你可以在rotate()中宣告一個新的中心點。如果你知道元素在SVG畫布中的尺寸和定位,你可以把它的中心設定為旋轉中心。

下面的例子是以當前使用者座標系中的(50,50)點為中心進行旋轉一組元素:

然而,如果你想要一個元素圍繞它的中心旋轉,你也許想要像CSS中一樣宣告中心為50% 50%;不幸的是,在rotate()函式中這樣做是不允許的-你必須用絕對座標。然而,你可以在CSS的transform屬性中使用transform-origin屬性。這篇文章後面會討論更多細節。

座標系變化

現在我們已經討論了所有可能的SVG變換函式,我們深入挖掘視覺部分和對SVG元素新增每個變換的效果。這是SVG變換最重要的部分。因此它們被稱為“座標系統變換”而不僅僅是“元素變換”。

在這個說明中,transform屬性被定義成兩個在被新增的元素上建立新使用者空間(當前座標系)之一-viewBox屬性是建立新使用者空間的兩個屬性中的另一個。所以到底是什麼意思呢?

這個行為類似於在HTML元素上新增CSS變換-HTML元素座標系發生了變換,當你把變換組合使用時最明顯。雖然在很多方面很相似,HTML和SVG的變換還是有一些不同。

主要的不同是座標系。HTML元素的座標系建立在元素自身智商。然而,在SVG中,元素的座標系最初是當前座標系或使用中的使用者空間。

當你在一個SVG元素上新增transform屬性,元素獲取當前使用的使用者座標系的一個“副本”。你可以當做給發生變換的元素建立一個新“層”,新層上是當前使用者座標系的副本(the viewBox)。

然後,元素新的當前座標系被在transform屬性中宣告的變換函式改變,因此導致元素自身的變換。這看起來好像是元素在變換後的座標系中重新繪製。

要理解如何新增SVG變換,讓我們從視覺化的部分開始。下面圖片是我們要研究的SVG畫布。

鸚鵡和小狗使我們要變換的元素(組<g>)。

灰色座標是通過viewBox建立的畫布的初始座標系。為了方便起見,我將不改變初始座標系-我用一個和視窗相同尺寸的viewBox,如你在上述程式碼中看到的一樣。

現在我們建立了畫布和初始使用者空間,讓我們開始變換元素。首先讓我們把鸚鵡向左移動150單位,向下移動200個單位。

當然,鸚鵡是由若干路徑和形狀組成的。只要把transform屬性新增到包含它們的組<g>上就行了;這會對整個形狀和路徑新增變換,鸚鵡會作為一個整體進行變換。檢視 article on structuring and grouping SVGs獲取更多資訊。

下面圖片展示了上述變換後的結果。鸚鵡的半透明版本是變換前的初始位置。

SVG中的變換和HTML元素上CSS中的一樣簡單直觀。我們之前提到在元素上新增transform屬性時會在元素上建立一個新的當前使用者座標系。下面圖片顯示了初始座標系的“副本”,它在鸚鵡元素髮生變換時被建立。注意觀察鸚鵡當前座標系是如何變換的。

這裡需要注意的非常重要的一點是建立在元素上的新的當前座標系是初始使用者座標系的複製,在裡面元素的位置得以保持。這意味著它不是建立在元素邊界盒上,或者新的當前座標系的尺寸受制於元素的尺寸。這就是HTML和SVG座標系之間的區別。

建立在變換元素上的新當前座標系不是建立在元素邊界盒上,或者新的當前座標系的尺寸受制於元素的尺寸。

我們把小狗變換到畫布的右下方時會更加明顯。試想我們想要把小狗向右移動50單位,向下移動50單位。這就是狗的最初的座標以及新的當前座標系(也因為狗改變)會如何顯示。注意小狗的新的座標系統的原點不在狗邊界盒子的左上角。另外注意狗和它新的座標系看起來它們好像移動到畫布新的一層上。

現在我們試一試其他事情。不再移動,試著縮放。我們將鸚鵡放大到兩倍尺寸:

放縮SVG元素和放縮HTML元素的結果不一樣。縮放後SVG元素的在視窗中的位置隨著縮放改變。下面圖片展示了把鸚鵡放大到兩倍時的結果。注意初始位置和尺寸,以及最終位置和尺寸。

從上面圖片中我們可以注意到不只鸚鵡的尺寸(寬和高)變成了兩倍,鸚鵡的座標(xy)也乘以了縮放因子(這裡是兩倍)。

這個結果的原因我們之前已經提到了:元素當前座標系發生變化,鸚鵡在新系統中繪製。所以,在這個例子中,當前座標系被縮放。這個效果類似於使用viewBox = "0 0 400 300",等於“放大”了座標系,因此把裡面的內容放大到雙倍尺寸(如果你還沒有讀過請檢視這個系列的第一部分)。

所以,如果我們把座標系變換形象化來展現當前變換系統中的鸚鵡,我們會得到以下結果:

鸚鵡的新的當前座標系統被縮放,同時“放大”鸚鵡。注意,在它當前的座標系中,鸚鵡沒有重新定位-只有縮放座標系統才會導致它在視窗中重定位。鸚鵡在新的縮放後的系統中按初始的xy座標被重繪。

讓我們嘗使用不同因子在兩個方向上縮放鸚鵡。如果我們新增transform="scale(2 0.5)縮放鸚鵡,我們把寬度變為兩倍高度為原來的一半。效果和新增viewBox="0 0 400 1200"類似。

注意一下鸚鵡在傾斜後的座標系中的位置,並且把它和初始系統(半透明的鸚鵡)中的位置做比較:xy位置座標保持不變。

在SVG中傾斜元素也導致元素被“移動”,因為它當前的座標系統被傾斜了。

試想我們使用skewX函式沿x軸給一隻狗增加一個傾斜變化。我們在垂直方向上把狗傾斜了25度。

下列圖片展示了對小狗新增傾斜變換的結果。

注意到狗的位置對比初始位置也改變了,因為它的座標系也被傾斜了。

下面的圖片展示了同樣角度的情況下使用skewY()而不是skewX傾斜狗的情況:

最後,讓我們嘗試旋轉鸚鵡。旋轉預設的中心是當前使用者座標系的左上角。新的建立在旋轉元素上的當前系統也被旋轉了。在下面的例子中,我們將把鸚鵡旋轉45度。旋轉方向為順時針。

新增上述變換的結果如下:

你很可能想要圍繞預設座標系原點之外的點來旋轉一個元素。在transform屬性中使用rotate()函式,你可以宣告這個點。試想在這個例子中我們想按照它自己的中心旋轉這個鸚鵡。根據鸚鵡的寬、高以及位置,我精確計算出它的中心在(150,170)。這個鸚鵡可以圍著它的中心旋轉。

在這個時候,這隻鸚鵡會被旋轉並且看起來如下:

我們說變換新增在座標系上,因此,元素最終被影響並且發生變換。那麼究竟如何改變旋轉中心工作在座標系的原點(0,0)的點呢?

當你改變中心或者旋轉時,座標系被變換或者旋轉特定角度,然後再次根據宣告的旋轉中心產生特定變換。在這個例子中:

被瀏覽器當成一系列的移動和旋轉等同於:

當前座標系變換到你想要的中心店。然後旋轉宣告的角度。最終系統被負值變換。上述新增到系統的變換如下:

在我們進行下一部分討論巢狀和組合變換前,我想請大家注意建立在變換元素上的當前使用者座標系是獨立於建立在其他變換元素之上的其他座標系的。下列圖片展示了建立在狗和鸚鵡上的兩個座標系,以及它們之間是如何保持獨立的。

另外注意每個當前座標系仍然處於在外層<svg>容器中使用viewBox屬性建立的畫布的主要座標系中。任何新增到viewBox上的變換會影響整個畫布以及所有裡面的元素,不管它們是否建立了自己的座標系。

例如,以下是把整個畫布的使用者空間從viewBox="0 0 800 600"改成 viewBox="0 0 600 450"的結果。整個畫布被縮放,保持任何新增到獨立元素上的變換。

巢狀和組合變換

很多時候你可能想要在一個元素上新增多個變換。新增多個變換意味著“組合”變換。

當變換組合時,最重要的是意識到,和HTML元素變換一樣,當這個系統發生了之前的變換後在新增下一個變換到座標系中。

例如,如果你要在一個元素上新增旋轉,接下來移動,移動變換會根據新的座標系統,而不是初始的沒有旋轉時的系統。

下面了例子就是做了這件事。我們先新增旋轉,然後沿x軸使用transform="rotate(45 150 170) translate(200)"把鸚鵡移動200個單位。

取決於最終的位置和變換,你可以根據需要組合變換。總是記住座標系。

注意當你傾斜一個元素-以及它的座標系統-座標系統不再是最初的那個,座標系不再會按照最初的來計算-它將會是傾斜後的座標系。簡單來說,這意味著座標系原點不再是90度的角,新的座標會根據新的角度來計算。

當變換元素的子元素也需要變換時會發生變換巢狀。新增到子元素上的變換會累積父元素上新增的變換和它本身的變換。

所以,效果上來說,巢狀變化類似於組合:唯一區別是不像在一個元素上新增一系列的變化,它自動從父元素上獲得變換,最後執行新增在它自身的變換,就像我們在上面新增的變換一樣-一個接一個。

這對於你想要根據另外一個元素變換一個元素時尤其有用。例如,試想你想要給小狗的尾巴設定一個動畫。這個尾巴是#dog組的後代。

試想我們變換dog組;圍繞某一點把它的身體旋轉一定角度,然後我們想要再把尾巴旋轉一定角度。

當尾巴被旋轉後,它從祖先(#body)身上“繼承”了變換座標系,也從祖先(#dog)身上繼承了變換座標系,然後旋轉(和#body組一樣的旋轉)然後在發生自身的旋轉。這裡新增的一系列變換的效果類似於我們之前在上述組合變換例子中解釋的。

所以,你看,在#tail上巢狀變換實際上和組合變換有一樣的效果。

使用CSS屬性變換SVGs

在SVG2中,transform屬性簡稱transform屬性;因為SVG變換已經被引入CSS3變換規範中。後者結合了SVG變化,CSS2 2D變換和CSS 3D變換規範,並且把類似transform-origin 和 3D transformations引入了SVG。

宣告在CSS變換規範中的CSS變換屬性可以被新增到SVG元素上。然而,transform屬性函式值需要遵循CSS規範中的語法宣告:函式引數必須逗號隔開-空格隔開是不允許的,但是你可以在逗號前後引用一兩個空格;rotate()函式不接受<cx><cy>值-旋轉中心使用transform-origin屬性宣告。另外,CSS變換函式接受角度和座標單位,例如角度的rad(radians)和座標的px,em等。

使用CSS來旋轉一個SVG元素看起來如下:

SVG元素也可以使用CSS 3D變換在三維空間中變換。依然要注意座標系,然而,不同於建立在HTML元素上的座標系。這意味著3D旋轉看起來也不同除非改變旋轉中心。

因為通過CSS來變換SVG元素非常類似於通過CSS來變換HTML元素-語法層面-在這篇文章中我將跳過這個部分。

另外,在寫這篇文章的時候,在一些瀏覽器中實現一些特性是不可能的。因為瀏覽器支援改變很快,我建議你實驗一下這些屬性來決定哪些可以工作哪些不可以,決定什麼現在可以用什麼不可以。

注意一旦CSS變換可以完全實現在SVG上,我依然建議你使用CSS變換函式語法即使你用transform屬性的形式新增變換。也就是說,上面提到的transform屬性函式的語法還是有效的。

動畫transform

SVG變換可以變成動畫,就像CSS變換一樣。如果你使用CSS transform屬性來產生SVG變換,你可以像在HTML元素上建立CSS變換動畫一樣使用CSS動畫把這些變換變成動畫。

SVGtransform屬性可以用SVG<animateTransform>元素來做成動畫。<animateTransform>元素是SVG中三個用來給不同的SVG屬性設定動畫的元素之一。

關於<animateTransform>元素的詳細內容不在本片文章的討論範圍內。閱讀我寫的關於不同SVG動畫元素的文章,包括<animateTransform>

最後的話

學習SVGs一開始可能非常困惑,如果對於座標系變換裡的內容不是很清楚,尤其是如果你帶著CSS HTML變換的背景知識,自然而然希望SVG元素和HTML元素的變換一樣。

然而,一旦你意識到它們的工作方式,你能更好得控制SVG畫布,並且輕易操縱元素。

這一系列的最後部分,我將討論巢狀SVGs和建立新的viewports和viewboxes。敬請關注!

相關文章