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

轉自: http://www.w3cplus.com/html5/svg-transformations.html


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

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

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

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值是可選的,如果省略,默認值爲0txty值可以通過空格或者逗號分隔,它們在函數中不代表任何單位-它們默認等於當前用戶座標系單位。

下面的例子把一個元素向右移動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值是可選的,如果省略默認值等於sxsxsy可以用空格或者逗號分隔,它們是無單位值。

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

<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元素也可以被傾斜,要傾斜一個元素,你可以使用一個或多個傾斜函數skewXskewY

skewX(<skew-angle>)
skewY(<skew-angle>)

函數skewX聲明一個沿x軸的傾斜;函數skewY聲明一個沿y軸的傾斜。

傾斜角度聲明是無單位角度的默認是度。

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

Rotation

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

rotate(<rotate-angle> [<cx> <cy>])

rotate()函數對於給定的點和旋轉角度值執行旋轉。不像CSS3中的旋轉變換,不能聲明除degress之外的單位。角度值默認無單位,默認單位是度。

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

在函數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畫布。

transform

鸚鵡和小狗使我們要變換的元素(組<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>

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

transform

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

transform

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

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

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

transform

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

<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元素的在視窗中的位置隨着縮放改變。下面圖片展示了把鸚鵡放大到兩倍時的結果。注意初始位置和尺寸,以及最終位置和尺寸。

transform

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

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

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

transform

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

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

transform

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

在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>

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

transform

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

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

transform

最後,讓我們嘗試旋轉鸚鵡。旋轉默認的中心是當前用戶座標系的左上角。新的建立在旋轉元素上的當前系統也被旋轉了。在下面的例子中,我們將把鸚鵡旋轉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

你很可能想要圍繞默認座標系原點之外的點來旋轉一個元素。在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>

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

transform

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

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

<g id="parrot" transform="rotate(45 150 170)">

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

<g id="parrot" transform="translate(150 170) rotate(45) translate(-150 -170)">

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

transform

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

transform

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

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

transform

嵌套和組合變換

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

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

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

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

transform

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

注意當你傾斜一個元素-以及它的座標系統-座標系統不再是最初的那個,座標系不再會按照最初的來計算-它將會是傾斜後的座標系。簡單來說,這意味着座標系原點不再是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。敬請關注!

本文根據SaraSoueidan的《Understanding SVG Coordinate Systems and Transformations (Part 2) — The transform Attribute》一文所譯,整個譯文帶有我們自己的理解與思想,如果譯得不好或有不對之處還請同行朋友指點。如需轉載此譯文,需註明英文出處http://sarasoueidan.com/blog/svg-transformations/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章