SVG元素可以通過縮放,移動,傾斜和旋轉來變換-類似HTML元素使用CSS transform來變換。然而,當涉及到坐標系時這些變換所產生的影響必然有一定差別。在這篇文章中我們討論SVG的transform
屬性和CSS屬性,包括如何使用,以及你必須知道的關於SVG坐標系變換的知識。
這是我寫的SVG坐標系統和變換部分的第二篇。在第一篇中,包括了任何要理解SVG坐標系統基礎的需要知道的內容;更具體的是, SVG viewport, viewBox
和 preserveAspectRatio
屬性。
- 理解SVG坐標系和變換(第一部分)-viewport,
viewBox
,和preserveAspectRatio
- 理解SVG坐標系和變換(第二部分)-
transform
屬性 - 理解SVG坐標系和變換(第三部分)-建立新視窗
這一部分我建議你先閱讀第一篇,如果沒有,確保你在閱讀這篇之前已經讀了第一篇。
transform
屬性值
tranform
屬性用來對一個元素聲明一個或多個變換。它輸入一個帶有順序的變換定義列表的<transform-list>
值。每個變換定義由空格或逗號隔開。給元素添加變換看起來如下:
有效地SVG變換有:旋轉, 縮放, 移動, 和傾斜。transform
屬性中使用的變換函數類似於CSS中transform屬性使用的CSS變換函數,除了參數不同。
注意下列的函數語法定義只在transform
屬性中有效。查看section about transforming SVGs with CSS properties獲取關於CSS變換屬性中使用的語法信息。
Matrix
你可以使用matrix()
函數在SVG元素上添加一個或多個變換。matrix
變換語法如下:
matrix(<a> <b> <c> <d> <e> <f>)
上述聲明通過一個有6個值的變換矩陣聲明一個變換。matrix(a,b,c,d,e,f)
等同於添加變換matrix[a b c d e f]
。
如果你不精通數學,最好不要用這個函數。對於那些精通的人,你可以在這里閱讀更多關於數學的內容。因此這個函數很少使用-我將忽略來討論其他變換函數。
Translation
要移動SVG元素,你可以用translate()
函數。translate
函數的語法如下:
translate(<tx> [<ty>])
translate()
函數輸入一個或兩個值得來聲明水平和豎直移動值。tx
代表x
軸上的translation
值;ty
代表y
軸上的translation
值。
ty
值是可選的,如果省略,默認值為0
。tx
和ty
值可以通過空格或者逗號分隔,它們在函數中不代表任何單位-它們默認等於當前用戶坐標系單位。
下面的例子把一個元素向右移動100
個用戶單位,向下移動300
個用戶單位。
<circle cx="0" cy="0" r="100" transform="translate(100 300)" />
上述代碼如果以translate(100, 300)
用逗號來分隔值的形式聲明一樣有效。
Scaling
你可以通過使用scale()
函數變換來向上或者向下縮放來改變SVG元素的尺寸。scale
變換的語法是:
scale(<sx> [<sy>])
scale()
函數輸入一個或兩個值來聲明水平和豎直縮放值。sx
代表沿x軸的縮放值,用來水平延長或者拉伸元素;sy
代表沿y軸縮放值,用來垂直延長或者縮放元素。
sy
值是可選的,如果省略默認值等於sx
。sx
和sy
可以用空格或者逗號分隔,它們是無單位值。
下面例子把一個元素的尺寸根據最初的尺寸放大兩倍:
<rect width="150" height="100" transform="scale(2)" x="0" y="0" />
下列例子把一個元素縮放到最初寬度的兩倍,並且把高度壓縮到最初的一半:
<rect width="150" height="100" transform="scale(2 0.5)" x="0" y="0" />
上述例子使用逗號分隔的值例如scale(2, .5)
仍然有效。
這里需要注意當SVG元素縮放時,整個坐標系被縮放,導致元素在視窗中重新定位,現在不用擔心這些,我們會在下一節中討論細節。
Skew
SVG元素也可以被傾斜,要傾斜一個元素,你可以使用一個或多個傾斜函數skewX
和 skewY
。
skewX(<skew-angle>) skewY(<skew-angle>)
函數skewX
聲明一個沿x軸的傾斜;函數skewY
聲明一個沿y軸的傾斜。
傾斜角度聲明是無單位角度的默認是度。
注意傾斜一個元素可能會導致元素在視窗中重新定位。在下一節中有更多細節。
Rotation
你可以使用rotate()
函數來旋轉SVG元素。這個函數的語法如下:
rotate(<rotate-angle> [<cx> <cy>])
rotate()
函數對於給定的點和 旋轉角度
值執行旋轉。不像CSS3中的旋轉變換,不能聲明除degress之外的單位。角度值默認無單位,默認單位是度。
可選的cx
和cy
值代表無單位的旋轉中心點。如果沒有設置cx
和cy
,旋轉點是當前用戶坐標系的原點(查看第一部分如果你不知道用戶坐標系是什么。)
在函數rotate()
中聲明旋轉中心點一個快捷方式類似於CSS中設置transform: rotate()
和transform-origin
。SVG中默認的旋轉中心是當前使用的用戶坐標系的左上角,這樣也許你無法創建想要的旋轉效果,你可以在rotate()
中聲明一個新的中心點。如果你知道元素在SVG畫布中的尺寸和定位,你可以把它的中心設置為旋轉中心。
下面的例子是以當前用戶坐標系中的(50,50)
點為中心進行旋轉一組元素:
<g id="parrot" transform="rotate(45 50 50)" x="0" y="0"> <!-- elements making up a parrot shape --> </g>
然而,如果你想要一個元素圍繞它的中心旋轉,你也許想要像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>
)。
<svg width="800" height="800" viewBox="0 0 800 600"> <g id="parrot"> <!-- shapes and paths forming the parrot --> </g> <g id="dog"> <!-- shapes and paths forming the dog --> </g> </svg>
灰色坐標是通過viewBox
建立的畫布的初始坐標系。為了方便起見,我將不改變初始坐標系-我用一個和視窗相同尺寸的viewBox
,如你在上述代碼中看到的一樣。
現在我們建立了畫布和初始用戶空間,讓我們開始變換元素。首先讓我們把鸚鵡向右移動150
單位,向下移動200
個單位。
當然,鸚鵡是由若干路徑和形狀組成的。只要把transform
屬性添加到包含它們的組<g>
上就行了;這會對整個形狀和路徑添加變換,鸚鵡會作為一個整體進行變換。查看 article on structuring and grouping SVGs獲取更多信息。
<svg width="800" height="800" viewBox="0 0 800 600"> <g id="parrot" transform="translate(150 200)"> <!-- shapes and paths forming the parrot --> </g> <!-- ... --> </svg>
下面圖片展示了上述變換后的結果。鸚鵡的半透明版本是變換前的初始位置。
SVG中的變換和HTML元素上CSS中的一樣簡單直觀。我們之前提到在元素上添加transform
屬性時會在元素上創建一個新的當前用戶坐標系。下面圖片顯示了初始坐標系的“副本”,它在鸚鵡元素發生變換時被建立。注意觀察鸚鵡當前坐標系是如何變換的。
這里需要注意的非常重要的一點是建立在元素上的新的當前坐標系是初始用戶坐標系的復制,在里面元素的位置得以保持。這意味着它不是建立在元素邊界盒上,或者新的當前坐標系的尺寸受制於元素的尺寸。這就是HTML和SVG坐標系之間的區別。
建立在變換元素上的新當前坐標系不是建立在元素邊界盒上,或者新的當前坐標系的尺寸受制於元素的尺寸。
我們把小狗變換到畫布的右下方時會更加明顯。試想我們想要把小狗向右移動50
單位,向下移動50
單位。這就是狗的最初的坐標以及新的當前坐標系(也因為狗改變)會如何顯示。注意小狗的新的坐標系統的原點不在狗邊界盒子的左上角。另外注意狗和它新的坐標系看起來它們好像移動到畫布新的一層上。
現在我們試一試其他事情。不再移動,試着縮放。我們將鸚鵡放大到兩倍尺寸:
<svg width="800" height="800" viewBox="0 0 800 600"> <g id="parrot" transform="scale(2)"> <!-- shapes and paths forming the parrot --> </g> <!-- ... --> </svg>
放縮SVG元素和放縮HTML元素的結果不一樣。縮放后SVG元素的在視窗中的位置隨着縮放改變。下面圖片展示了把鸚鵡放大到兩倍時的結果。注意初始位置和尺寸,以及最終位置和尺寸。
從上面圖片中我們可以注意到不只鸚鵡的尺寸(寬和高)變成了兩倍,鸚鵡的坐標(x
和y
)也乘以了縮放因子(這里是兩倍)。
這個結果的原因我們之前已經提到了:元素當前坐標系發生變化,鸚鵡在新系統中繪制。所以,在這個例子中,當前坐標系被縮放。這個效果類似於使用viewBox = "0 0 400 300"
,等於“放大”了坐標系,因此把里面的內容放大到雙倍尺寸(如果你還沒有讀過請查看這個系列的第一部分)。
所以,如果我們把坐標系變換形象化來展現當前變換系統中的鸚鵡,我們會得到以下結果:
鸚鵡的新的當前坐標系統被縮放,同時“放大”鸚鵡。注意,在它當前的坐標系中,鸚鵡沒有重新定位-只有縮放坐標系統才會導致它在視窗中重定位。鸚鵡在新的縮放后的系統中按初始的x
和y
坐標被重繪。
讓我們嘗使用不同因子在兩個方向上縮放鸚鵡。如果我們添加transform="scale(2 0.5)
縮放鸚鵡,我們把寬度變為兩倍高度為原來的一半。效果和添加viewBox="0 0 400 1200"
類似。
注意一下鸚鵡在傾斜后的坐標系中的位置,並且把它和初始系統(半透明的鸚鵡)中的位置做比較:x
和y
位置坐標保持不變。
在SVG中傾斜元素也導致元素被“移動”,因為它當前的坐標系統被傾斜了。
試想我們使用skewX
函數沿x軸給一只狗增加一個傾斜變化。我們在垂直方向上把狗傾斜了25度。
<svg width="800" height="800" viewBox="0 0 800 600"> <!-- ... --> <g id="dog" transform="skewX(25)"> <!-- shapes and paths forming the dog --> </g> </svg>
下列圖片展示了對小狗添加傾斜變換的結果。
注意到狗的位置對比初始位置也改變了,因為它的坐標系也被傾斜了。
下面的圖片展示了同樣角度的情況下使用skewY()
而不是skewX
傾斜狗的情況:
最后,讓我們嘗試旋轉鸚鵡。旋轉默認的中心是當前用戶坐標系的左上角。新的建立在旋轉元素上的當前系統也被旋轉了。在下面的例子中,我們將把鸚鵡旋轉45度。旋轉方向為順時針。
<svg width="800" height="800" viewBox="0 0 800 600"> <g id="parrot" transform="rotate(45)"> <!-- shapes and paths forming the parrot --> </g> <!-- ... --> </svg>
添加上述變換的結果如下:
你很可能想要圍繞默認坐標系原點之外的點來旋轉一個元素。在transform
屬性中使用rotate()
函數,你可以聲明這個點。試想在這個例子中我們想按照它自己的中心旋轉這個鸚鵡。根據鸚鵡的寬、高以及位置,我精確計算出它的中心在(150,170)
。這個鸚鵡可以圍着它的中心旋轉。
<svg width="800" height="800" viewBox="0 0 800 600"> <g id="parrot" transform="rotate(45 150 170)"> <!-- shapes and paths forming the parrot --> </g> <!-- ... --> </svg>
在這個時候,這只鸚鵡會被旋轉並且看起來如下:
我們說變換添加在坐標系上,因此,元素最終被影響並且發生變換。那么究竟如何改變旋轉中心工作在坐標系的原點(0,0)
的點呢?
當你改變中心或者旋轉時,坐標系被變換或者旋轉特定角度,然后再次根據聲明的旋轉中心產生特定變換。在這個例子中:
<g id="parrot" transform="rotate(45 150 170)">
被瀏覽器當成一系列的移動和旋轉等同於:
<g id="parrot" transform="translate(150 170) rotate(45) translate(-150 -170)">
當前坐標系變換到你想要的中心店。然后旋轉聲明的角度。最終系統被負值變換。上述添加到系統的變換如下:
在我們進行下一部分討論嵌套和組合變換前,我想請大家注意建立在變換元素上的當前用戶坐標系是獨立於建立在其他變換元素之上的其他坐標系的。下列圖片展示了建立在狗和鸚鵡上的兩個坐標系,以及它們之間是如何保持獨立的。
另外注意每個當前坐標系仍然處於在外層<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組
的后代。
<svg width="800" height="800" viewBox="0 0 800 600"> <!-- ... --> <g id="dog" transform="translate(..)"> <!-- shapes and paths forming the dog --> <g id="head"> <!-- .. --> </g> <g id="body" transform="rotate(.. .. ..)"> <path id="tail" d="..." transform="rotate(..)"> <!-- animateTransform here --> </path> <g id="legs"> <!-- ... --> </g> </g> </g> </svg>
試想我們變換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元素看起來如下:
#parrot { transform-origin: 50% 50%; /* center of rotation is set to the center of the element */ transform: rotate(45deg); }
SVG元素也可以使用CSS 3D變換在三維空間中變換。依然要注意坐標系,然而,不同於建立在HTML元素上的坐標系。這意味着3D旋轉看起來也不同除非改變旋轉中心。
#SVGel { transform: perspective(800px) rotate3d(1, 1, 0, 45deg); }
因為通過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。敬請關注!