SVG綜述
位圖與矢量圖
HTML體系中,最常用的繪制矢量圖的技術是SVG和HTML5新增加的canvas元素。這兩種技術都支持繪制矢量圖和光柵圖。
SVG簡介
可縮放矢量圖形(Scalable Vector Graphics,簡稱SVG)是一種使用XML來描述二維圖形的語言(SVG嚴格遵從XML語法)。 SVG允許三種類型的圖形對象:矢量圖形形狀(例如由直線和曲線組成的路徑)、圖像和文本。 可以將圖形對象(包括文本)分組、樣式化、轉換和組合到以前呈現的對象中。 SVG 功能集包括嵌套轉換、剪切路徑、alpha 蒙板和模板對象。
SVG既可以說是一種協議,也可以說是一門語言;既是HTML的一個標準元素,也是一種圖片格式。
SVG與其它圖片格式的比較
SVG與其它的圖片格式相比,有很多優點(很多優點來源于矢量圖的優點):
- SVG文件是純粹的XML, 可被非常多的工具讀取和修改(比如記事本)。
- SVG 與JPEG 和GIF圖像比起來,尺寸更小,且可壓縮性更強。
- SVG 是可伸縮的,可在圖像質量不下降的情況下被放大,可在任何的分辨率下被高質量地打印。
- SVG 圖像中的文本是可選的,同時也是可搜索的(很適合制作地圖)。
- SVG 可以與 Java 技術一起運行。
- SVG 是開放的標準。
SVG與Flash的比較
SVG 的主要競爭者是Flash。與Flash相比,SVG 最大的優勢是它與其他標準(比如XSL和DOM)相兼容,操作方便,而Flash則是未開源的私有技術。其它的比如存儲的格式,動態生成圖形等方面,SVG也占有很大的優勢。
瀏覽器支持情況(http://caniuse.com/#cats=SVG)
√ IE 9+
√ Chrome 33.0+
√ Firefox 28.0+
√ Safari 7.0+
SVG簡單案例
- 瀏覽器直接打開
- 在 HTML 中使用標簽引用
效果: - 直接在 HTML 中使用 SVG 標簽
效果: - 作為 CSS 背景
效果:
因為我們SVG的尺寸是設置200x200所以這邊自動平鋪
SVG圖形繪制
基本形狀
SVG提供了很多的基本形狀,這些元素可以直接使用,這一點比canvas好多了。廢話不說了,直接看例子,這個最直接:
這段HTML顯示的結果如下:
這個例子畫了很多形狀:正常的矩形,帶圓角的矩形,圓形,橢圓形,直線,折線,多邊形,還有路徑。這些都屬于基本的圖形元素。雖然繪制一個圖形有很多種方式,比如矩形可以用rect實現,也可以用path等實現,但是我們還是應該盡量保持SVG的內容短小精悍,易于閱讀。
下面是每個形狀的使用說明(這里只介紹控制圖形形狀和位置的基本屬性,填充等屬性放到后面總結)。
矩形 - rect元素
這個元素有6個控制位置和形狀的屬性,分別是:
x:矩形左上角的坐標(用戶坐標系)的x值。
y:矩形左上角的坐標(用戶坐標系)的y值。
width:矩形寬度。
height:矩形高度。
rx:實現圓角效果時,圓角沿x軸的半徑。
ry:實現圓角效果時,圓角沿y軸的半徑。
比如上面例子中,實現了圓角效果,你也可以通過設置rx,ry為不同的值實現橢圓角效果。
圓 - circle元素
這個元素的屬性很簡單,主要是定義圓心和半徑:
r:圓的半徑。
cx:圓心坐標x值。
cy:圓心坐標y值。
橢圓 - ellipse元素
這個是更加通用的圓形元素,你可以分別控制半長軸和半短軸的長度,來實現不同的橢圓,很容易想到,當兩個半軸相等時,就是正圓形了。
rx:半長軸(x半徑)。
ry:半短軸(y半徑)。
cx:圓心坐標x值。
cy:圓心坐標y值。
直線 - line元素
直線需要定義起點與終點即可:
x1:起點x坐標。
y1:起點y坐標。
x2:終點x坐標。
y2:終點y坐標。
折線 - polyline元素
折線主要是要定義每條線段的端點即可,所以只需要一個點的集合作為參數:
points:一系列的用空格,逗號,換行符等分隔開的點。每個點必須有2個數字:x值和y值。所以下面3個點 (0,0), (1,1)和(2,2)可以寫成:"0 0, 1 1, 2 2"。
多邊形 - polygon元素
這個元素就是比polyline元素多做一步,把最后一個點和第一個點連起來,形成閉合圖形。參數是一樣的。
points:一系列的用空格,逗號,換行符等分隔開的點。每個點必須有2個數字:x值和y值。所以下面3個點 (0,0), (1,1)和(2,2)可以寫成:"0 0, 1 1, 2 2"。
路徑 - path元素
這個是最通用,最強力的元素了;使用這個元素你可以實現任何其他的圖形,不僅包括上面這些基本形狀,也可以實現像貝塞爾曲線那樣的復雜形狀;此外,使用path可以實現平滑的過渡線段,雖然也可以使用polyline來實現這種效果,但是需要提供的點很多,而且放大了效果也不好。這個元素控制位置和形狀的只有一個參數:
d:一系列繪制指令和繪制參數(點)組合成。
繪制指令分為絕對坐標指令和相對坐標指令兩種,這兩種指令使用的字母是一樣的,就是大小寫不一樣,絕對指令使用大寫字母,坐標也是絕對坐標;相對指令使用對應的小寫字母,點的坐標表示的都是偏移量。
絕對坐標繪制指令
這組指令的參數代表的是絕對坐標。假設當前畫筆所在的位置為(x0,y0),則下面的絕對坐標指令代表的含義如下所示:
指令 | 參數 | 說明 |
---|---|---|
M | x y | 將畫筆移動到點(x,y) |
L | x y | 畫筆從當前的點繪制線段到點(x,y) |
H | x | 畫筆從當前的點繪制水平線段到點(x,y0) |
V | y | 畫筆從當前的點繪制豎直線段到點(x0,y) |
A | rx ry x-axis-rotation large-arc-flag sweep-flag x y | 畫筆從當前的點繪制一段圓弧到點(x,y) |
C | x1 y1, x2 y2, x y | 畫筆從當前的點繪制一段三次貝塞爾曲線到點(x,y) |
S | x2 y2, x y | 特殊版本的三次貝塞爾曲線(省略第一個控制點) |
Q | x1 y1, x y | 繪制二次貝塞爾曲線到點(x,y) |
T | x y | 特殊版本的二次貝塞爾曲線(省略控制點) |
Z | 無參數 | 繪制閉合圖形,如果d屬性不指定Z命令,則繪制線段,而不是封閉圖形。 |
移動畫筆指令M,畫直線指令:L,H,V,閉合指令Z都比較簡單;下面重點看看繪制曲線的幾個指令。
繪制圓弧指令:A rx ry x-axis-rotation large-arc-flag sweep-flag x y
用圓弧連接2個點比較復雜,情況也很多,所以這個命令有7個參數,分別控制曲線的的各個屬性。下面解釋一下數值的含義:
rx,ry 是弧的半長軸、半短軸長度
x-axis-rotation 是此段弧所在的x軸與水平方向的夾角,即x軸的逆時針旋轉角度,負數代表順時針轉動的角度。
large-arc-flag 為1 表示大角度弧線,0 代表小角度弧線。
sweep-flag 為1代表從起點到終點弧線繞中心順時針方向,0 代表逆時針方向。
x,y 是弧終端坐標。
前兩個參數和后兩個參數就不多說了,很簡單;下面就說說中間的3個參數:
x-axis-rotation代表旋轉的角度,體會下面例子中圓弧的不同:
上面的HTML畫出下面的圖形:
從圖中可以看到橢圓旋轉參數的不同導致繪制的圓弧方向不同,當然這個參數對正圓來說沒有影響。
large-arc-flag和sweep-flag控制了圓弧的大小和走向,體會下面例子中圓弧的不同:
這個HTML繪制了下面的幾幅圖:
從上面可以看出,這幾個參數其實是唯一確定一段圓弧需要的參數。這里也看到,SVG中的圓弧比canvas中的復雜一些。
繪制三次貝塞爾曲線指令:C x1 y1, x2 y2, x y
三次貝塞爾曲線有兩個控制點,就是(x1,y1)和(x2,y2),最后面(x,y)代表曲線的終點。體會下面的例子:
這個HTML片段繪制了下面的圖形:
從上面我們可以看到,控制點控制了曲線的弧度。
特殊版本的三次貝塞爾曲線:S x2 y2, x y
很多時候,為了繪制平滑的曲線,需要多次連續繪制曲線。這個時候,為了平滑過渡,常常第二個曲線的控制點是第一個曲線控制點在曲線另外一邊的映射點。這個時候可以使用這個簡化版本。這里要注意的是,如果S指令前面沒有其他的S指令或C指令,這個時候會認為兩個控制點是一樣的,退化成二次貝塞爾曲線的樣子;如果S指令是用在另外一個S指令或者C指令后面,這個時候后面這個S指令的第一個控制點會默認設置為前面的這個曲線的第二個控制點的一個映射點,體會一下:
這個HTML片段繪制如下圖形:
上面的S指令只有第二個控制點,第一個控制點采用了前面的曲線指令的第二個控制點的一個映射點。
繪制二次貝塞爾曲線指令:Q x1 y1, x y , T x y (特殊版本的二次貝塞爾曲線)
二次貝塞爾曲線只有一個控制點(x1,y1),通常如下圖所示:
如果是連續的繪制曲線,同樣可以使用簡化版本T。同樣的,只有T前面是Q或者T指令的時候,后面的T指令的控制點會默認設置為前面的曲線的控制點的映射點,體會一下:
同樣的,如果T指令前面不是Q或者T指令,則認為無控制點,畫出來的是直線。
相對坐標繪制指令
與絕對坐標繪制指令的字母是一樣的,只不過全部是小寫表示。這組指令的參數中涉及坐標的參數代表的是相對坐標,意思就是參數代表的是從當前點到目標點的偏移量,正數就代表向軸正向偏移,負數代表向反向偏移。不過對Z指令來說,大小寫沒有區別。
這里要注意,絕對坐標指令與相對坐標指令是可以混合使用的。有時混合使用可以帶來更靈活的畫法。
SVG path繪制注意事項:
繪制帶孔的圖形時要注意:外層邊的繪制需要是逆時針順序的,里面的洞的邊的順序必須是順時針的。只有這樣繪制的圖形填充效果才會正確。
SVG文本與圖像
SVG中渲染文本
SVG的強大能力之一是它可以將文本控制到標準HTML頁面不可能有的程度,而無須求助圖像或其它插件。任何可以在形狀或路徑上執行的操作(如繪制或濾鏡)都可以在文本上執行。盡管SVG的文本渲染如此強大,但是還是有一個不足之處:SVG不能執行自動換行。如果文本比允許空間長,則簡單地將它切斷。多數情況下,創建多行文本需要多個文本元素。
此外,可以使用 tspan 元素可以將文本元素分成幾部分,允許每部分有各自的樣式。
還有,在text元素中,空格的處理與HTML類似:換行和回車變成空格,而多個空格壓縮成單個空格。
直接顯示在圖片中的文本 - text元素
直接顯示文本,可以使用text元素,例子如下:
如上面的例子中所示,text元素可以設置下列的屬性:
- x,y是文本位置坐標。
- text-anchor是文本顯示的方向,其實也就是位置(x,y)處于文本的位置。這個屬性有start,middle和end三種值。
- start表示文本位置坐標(x,y)位于文本的開始處,文本從這點開始向右挨個顯示。
- middle表示(x,y)位于文本中間處,文本向左右兩個方向顯示,其實就是居中顯示。
- end表示(x,y)點位于文本結尾,文本向左挨個顯示。
除了這些屬性,下面的這些屬性都既可以在CSS中指定,也可以直接在屬性中指定:
- fill,stroke:填充和描邊顏色,具體使用在后面總結。
- font的相關屬性:font-family, font-style, font-weight, font-variant, font-stretch, font-size, font-size-adjust, kerning, letter-spacing, word-spacing and text-decoration。
文本區間 - tspan元素
這個元素是text元素的強力補充;它用于渲染一個區間內的文本;它只能出現在text元素或者tspan元素的子元素中。典型的用法就是強調顯示部分文本。例如:
tspan元素有下列的屬性可以設置:
- x,y用于設置包含的文本的絕對坐標值,這個值會覆蓋默認的文本位置。這些屬性可以包含一系列數字,這些數字會應用到每個對應的單個字符。沒有對應設置的字符會緊跟前一個字符。例如:
- dx,dy用于設置包含的文本相對于默認的文本位置的偏移量。這些屬性同樣可以包含一系列數字,每個都會應用到對應的字符。沒有對應設置的字符會緊跟前一個字符。你可以把上面的例子中的x換成dx看看效果。
- rotate用于設置字體的旋轉角度。這個屬性頁可以包含一系列數字,應用到每個字符。沒有對應設置的字符會使用最后設置的那個數字。
- textLength:這是最令人費解的屬性,據說設置完以后,渲染發現文本的長度與這個值不一致時,會以這個長度為準。但是我沒有試出來效果。
文本引用 - tref元素
這個元素允許引用定義過的文本,并高效的拷貝到當前位置,通常配合xlink:href指定目的元素。因為是拷貝過來的,所以使用css修改當前文本的時候,不會修改原來的文本?蠢樱
文本路徑 - textPath元素
這個比較有意思,效果也很酷,能做出很多的藝術效果;這個元素從它的xlink:href屬性獲取指定的路徑并把文本對齊到這個路徑上,看例子:
SVG中渲染圖片 - image元素
SVG中的image元素可以直接支持顯示光柵圖片,使用很簡單?聪旅娴睦樱
這里需要注意幾點:
- 如果沒有設置x或y坐標,則默認是0。
- 如果沒有設置width或height,則默認也是0.
- 如果顯式的設置width或height為0,則會禁止渲染這幅圖片。
- 圖片的格式支持png,jpeg,jpg,svg等等,所以svg是支持嵌套svg的。
- image與其他元素一樣,是svg的常規元素,所以它支持所有的裁剪,蒙板,濾鏡,旋轉等效果。
SVG筆畫與填充
前面我們重點都在總結各類形狀,文本和圖片,接下來,我們還是和討論canvas一樣,總結一下顏色處理,也就是填充和邊框效果;你會發現這里的內容與canvas基本上是一致的。這些屬性既可以以屬性的形式寫在元素中,也可以以CSS的形式保存(這是與canvas不一樣的地方)。
填充色 - fill屬性
這個屬性使用設置的顏色填充圖形內部,使用很簡單,直接把顏色值賦給這個屬性就可以了?蠢樱
上面例子中畫了一個紅色藍邊的矩形。注意幾點:
- 如果不提供fill屬性,則默認會使用黑色填充,如果要取消填充,需要設置成none。
- 可以設置填充的透明度,就是fill-opacity,值的范圍是0到1。
- 稍微復雜一點的是fill-rule屬性。這個屬性定義了判斷點是不是屬于填充范圍的算法;除了inherit這個值外,還有兩個取值:
nonzero:這個值采用的算法是:從需要判定的點向任意方向發射線,然后計算圖形與線段交點的處的走向;計算結果從0開始,每有一個交點處的線段是從左到右的,就加1;每有一個交點處的線段是從右到左的,就減1;這樣計算完所有交點后,如果這個計算的結果不等于0,則該點在圖形內,需要填充;如果該值等于0,則在圖形外,不需要填充?聪旅娴氖纠
evenodd:這個值采用的算法是:從需要判定的點向任意方向發射線,然后計算圖形與線段交點的個數,個數為奇數則改點在圖形內,需要填充;個數為偶數則點在圖形外,不需要填充?聪聢D的示例:
邊框色 - stroke屬性
上面的例子中已經用到了stroke屬性,這個屬性使用設置的值畫圖形的邊框,使用起來也很直接,把顏色值賦給它就可以了。注意:
1. 如果不提供stroke屬性,則默認不繪制圖形邊框。
2. 可以設置邊的透明度,就是stroke-opacity,值的范圍是0到1。
實際上,邊的情況比圖形內部稍微復雜一點,因為邊除了顏色,還有"形狀"需要定義。
線的端點 - stroke-linecap屬性
這個屬性定義了線段端點的風格,這個屬性可以使用butt,square,round三個值?蠢樱
這段代碼繪制了3條使用不同風格線端點的線,從圖中我們可以很容易看出3中風格的不同。
線的連接 - stroke-linejoin屬性
這個屬性定義了線段連接處的風格,這個屬性可以使用miter,round,bevel三個值?蠢樱
從上面的圖中我們很容易看到3中風格的不同。
線的虛實 - stroke-dasharray屬性
這個屬性可以設置線段采用何種虛實線?蠢樱
這個屬性是設置一些列數字,不過這些數字必須是逗號隔開的。屬性中當然可以包含空格,但是空格不作為分隔符。每個數字定義了實線段的長度,分別是按照繪制、不繪制這個順序循環下去。所以上面的例子中繪制的線是畫5單位的實線,留5單位的空格,再畫5單位的實線...這樣一直下去。
除了這些常用的屬性,還有下列屬性可以設置:
stroke-miterlimit:這個和canvas中的一樣,它處理什么時候畫和不畫線連接處的miter效果。
stroke-dashoffset:這個屬性設置開始畫虛線的位置。
使用CSS展示數據
HTML5強化了DIV+CSS的思想,所以展示數據的部分還可以交給CSS處理。與普通HTML元素相比,只不過是 background-color和border換成了fill和stroke。其他的大多都差不多。簡單看個例子:
是不是很熟悉,就是這么簡單的。
SVG顏色的表示
SVG和canvas中是一樣的,都是使用標準的HTML/CSS中的顏色表示方法,這些顏色都可以用于fill和stroke屬性。
基本有下面這些定義顏色的方式:
- 顏色名字: 直接使用顏色名字red, blue, black...
- rgba/rgb值: 這個也很好理解,例如#ff0000,rgba(255,100,100,0.5)。
- 十六進制值: 用十六進制定義的顏色,例如#ffffff。
- 漸變值:這個也與canvas中一樣,支持兩種漸變色:線性漸變,環形漸變。如下圖所示:
- 圖案填充:使用自定義的圖案作為填充色。
前面幾種都很簡單,重點看下后面兩種填充色。
線性漸變
使用linearGradient元素即可定義線性漸變,每一個漸變色成分使用stop元素定義?聪旅娴睦樱
從上面的例子看到,除了元素名字和一些特別的成員,其他的所有都和線性漸變一樣,包括stop的定義,必須放到defs中,必須給它設置id,使用url(#id)去賦值等。這些特別的成員如下:
offset屬性:這個和線性漸變的值是一樣,但是含義不一樣。在環形漸變中,0%代表圓心處,這個很好理解。
cx,cy,r屬性:其實也很好理解,環形漸變,當然要定義環的圓心和半徑了,體會一下上面例子中圓的大小和位置就能理解了。
fx,fy屬性:定義顏色中心(焦點)處的位置,也就是漸變色最濃處的坐標,在上面例子中,紅色最紅的是圓心,這是默認效果;如果想改變一下,就可以設置fx,fy坐標值。
不過這里需要注意一下上面cx,cy,r,fx,fy的值,你會發現它們都是小數,那么單位是什么呢?
這個需要先了解另外一個相關的屬性:gradientUnits,它定義了定義漸變色使用的坐標單位。這個屬性有2個可用值:userSpaceOnUse和objectBoundingBox。
objectBoundingBox是默認值,它使用的坐標都是相對于對象包圍盒的(方形包圍盒,不是方形包圍盒的情況比較復雜,略過),取值范圍是0到1。例如上例中的cx,cy的坐標值(0.25,0.25)。意味著這個圓心是在包圍盒的左上角1/4處,半徑0.25意味著半徑長是對象方形包圍盒長的1/4,就像你們圖中看到的那樣。
userSpaceOnUse表示使用的是絕對坐標,使用這個設置的時候,你必須要保證漸變色和填充的對象要保持在一個位置。
再看下面這個例子,注意gradientUnits屬性默認值是objectBoundingBox:
此外,還有漸變色元素還有一些變換的屬性,如gradientTransform,這個不是這里的重點,后面會總結變換。
另外一個可能用到的屬性是spreadMethod屬性,這個屬性定義了漸變色到達它的終點時應該采取的行為。該屬性有3個可選值:pad(默認值),reflect,repeat。pad不用說了,屬于自然過渡,漸變色結束以后,使用最后一個成員色直接渲染對象剩下的部分。refect會讓漸變色繼續,只不過漸變色會反向繼續渲染,從最后一個顏色開始到第一個顏色這個順序渲染;等到再次到達漸變色終點時,再反序,如此這般指導對象填充完畢。repeat也會讓漸變色繼續渲染,但是不會反序,還是一遍一遍從第一種顏色到最后一種顏色渲染。效果圖如下所示:
看一段重復渲染的代碼:
紋理填充
紋理填充也是一種流行的填充方式,在SVG中,可以使用pattern創建一個紋理,然后用這個pattern去填充別的對象。直接看例子:
例子看起來很簡單,由漸變色創建pattern,然后使用pattern
填充矩形。這里需要注意:
1.不同的瀏覽器填充這個pattern的時候效果不一樣。
2.pattern也需要定義id。
3.pattern也必須要定義在defs中。
4.pattern的使用也是把url(#id)直接賦值給fill或stroke。
上面這些都是很簡單的,我們重點看一下例子中的坐標表示情況,坐標在pattern中比較復雜。
pattern中包含兩個相關屬性:patternUnits和patternContentUnits屬性;這兩個屬性的取值都還是只有2個:objectBoundingBox和userSpaceOnUse,這兩個值的含義上面以及講過了。這里容易混淆的是這兩個屬性的默認值不同,但是當你理解這么做的原因以后,你又會發現這么做還真是有道理。
patternUnits屬性
這個屬性與Gradient的gradientUnits屬性是一樣的,默認采用objectBoundingBox。受這個屬性影響的屬性有x,y,width,height,這4個屬性分別定義了pattern的起點,寬高度。它們都采用了相對值,例子中想要在水平和豎直方向上都填充4次,所以width和height都設為了0.25。
patternContentUnits屬性
這個屬性的默認值正好相反,采用userSpaceOnUse。這個屬性描述了pattern中繪制的形狀(比如上面的rect,circle)的坐標系統。也就是說在默認情況下,你在pattern中繪制的形狀和pattern自身的大小/位置使用了不一樣的坐標系?紤]上面例子中的情況,我們想填充一個200*200的矩形,而且每個方向重復4次。這就意味著每個pattern是50*50的,那么pattern里面的兩個矩形和一個圓形就是畫在這個50*50的矩形中。這樣我們就能理解上面pattern中的矩形和圓的坐標了。此外,這個例子中的pattern為了居中,需要偏移10px后開始渲染,而這個值是受patternUnits屬性制約的,所以默認情況下,x,y值就為:10/200=0.05。
SVG坐標與變換
坐標系統
SVG存在兩套坐標系統:視窗坐標系與用戶坐標系。默認情況下,用戶坐標系與視窗坐標系的點是一一對應的,都為原點在視窗的左上角,x軸水平向右,y軸豎直向下;如下圖所示:
SVG的視窗位置一般是由CSS指定,尺寸由SVG元素的屬性width和height設置,但是如果SVG是存儲在embedded對象中(例如object元素,或者其他SVG元素),而且包含SVG的文檔是用CSS或者XSL格式化的,并且這些外圍對象的CSS或者其他指定尺寸的值已經可以計算出視窗的尺寸了,則此時會使用外圍對象的尺寸。
這里需要區分視窗,視窗坐標系,用戶坐標系的概念:
視窗:指的是網頁上面可視的矩形局域,長度和寬度都是有限的,這個區域一般與外圍對象的尺寸有關。
視窗坐標系:本質是一個坐標系,有原點,x軸與y軸;而且在兩個方向上是無限延伸的。默認情況下,原點在視窗的左上角,x軸水平向右,y軸豎直向下?梢詫@個坐標系的點進行變換。
用戶坐標系:本質是一個坐標系,有原點,x軸與y軸;而且在兩個方向上是無限延伸的。默認情況下,原點在視窗的左上角,x軸水平向右,y軸豎直向下?梢詫@個坐標系的點進行變換。
默認情況下,視窗坐標系與用戶坐標系是重合的,但是這里需要注意,視窗坐標系屬于的是創建視窗的元素,視窗坐標系確定好以后,整個視窗的坐標基調就確定了。但是用戶坐標系是屬于每個圖形元素的,只要圖形進行了坐標變換,就會創建新的用戶坐標系,這個元素中所有的坐標和尺寸都使用這個新的用戶坐標系。
簡單點說:視窗坐標系描述了視窗中所有元素的初始坐標概況,用戶坐標系描述了每個元素的坐標概況,默認情況下,所有元素都使用默認的與視窗坐標系重合的那個用戶坐標系。
坐標空間變換
讓我們回顧一下canvas用戶坐標的變換,它們是通過平移,縮放,旋轉函數實現的;每次變換后對以后繪制的圖形都起作用,除非再次進行變換,這是"當前"用戶坐標系統的概念。canvas只有唯一一個用戶坐標系。
在SVG中,情況完全不同。SVG本身作為一種向量圖元素,它的兩個坐標系統本質上都可以算作"用戶坐標系統";SVG的兩個坐標空間都是可以變換的:視窗空間變換和用戶空間變換。視窗空間變換由相關元素(這些元素創建了新的視窗)的屬性viewBox控制;用戶空間變換由圖形元素的transform屬性控制。視窗空間變換應用于對應的整個視窗,用戶空間變換應用于當前元素及其子元素。
視窗變換 - viewBox屬性
所有的能建立一個視窗的元素,再加上marker,pattern,view元素,都有一個viewBox屬性。
viewBox屬性值的格式為(x0,y0,u_width,u_height),每個值之間用逗號或者空格隔開,它們共同確定了視窗顯示的區域:視窗左上角坐標設為(x0,y0)、視窗的寬設為u_width,高為u_height;這個變換對整個視窗都起作用。
這里一定不要混淆:視窗的大小和位置已經由創建視窗的元素和外圍的元素共同確定了(例如最外層的svg元素建立的視窗由CSS,width和height確定),這里的viewBox其實是設置這個確定的區域能顯示視窗坐標系的哪個部分。
viewBox的設置其實是包含了視窗空間的縮放和平移兩種變換。
變換的計算也很簡單:以最外層的svg元素的視窗為例,假設svg的寬與長設置為width,height,viewBox的設置為(x0,y0,u_width,u_height)。則繪制的圖形,寬和高的縮放比例分別為:width/u_width, height/u_height。視窗的左上角的坐標設置為了(x0,y0)。
體會下面幾種代碼繪出的結果的不同:
上面的例子繪制的圖中你可以看到綠色和紅色的矩形,這種情況下視窗坐標系的點還是與視窗上的點是一一對應的,這個也是默認情況。
上面的例子繪制的圖中這個你只能看到綠色的矩形,而且綠色的矩形顯示在屏幕上是200*200像素的,這個時候坐標點已經不是一一對應了,圖被放大了。
上面的例子繪制的圖中,視窗坐標系的單位被縮小,所以兩個矩形都縮小了。
在日常工作中,我們經常需要完成的一個任務就是縮放一組圖形,讓它適應它的父容器。我們可以通過設置viewBox屬性達到這個目的。
能建立新視窗的元素
任何時候,我們都可以嵌套視窗。創建新的視窗的時候,也會創建新的視窗坐標系和用戶坐標系,當然也包括裁減路徑也會創建新的。下列是能建立新視窗的元素列表:
svg:svg支持嵌套。
symbol:當被use元素實例化的時候創建新的視窗。
image:引用svg元素時會創建新視窗。
foreignObject:創建新視窗去渲染里面的對象。
保持縮放的比例 - preserveAspectRatio屬性
有些時候,特別是當使用viewBox的時候,我們期望圖形占據整個視窗,而不是兩個方向上按相同的比例縮放。而有些時候,我們卻是希望圖形兩個方向是按照固定的比例縮放的。使用屬性preserveAspectRatio就可以達到控制這個的目的。
這個屬性是所有能建立一個新視窗的元素,再加上image,marker,pattern,view元素都有的。而且preserveAspectRatio屬性只有在該元素設置了viewBox以后才會起作用。如果沒有設置viewBox,則preserveAspectRatio屬性會被忽略。
屬性的語法如下:preserveAspectRatio="[defer]
注意3個參數之間需要使用空格隔開。
defer:可選參數,只對image元素有效,如果image元素中preserveAspectRatio屬性的值以"defer"開頭,則意味著image元素使用引用圖片的縮放比例,如果被引用的圖片沒有縮放比例,則忽略"defer"。所有其他的元素都忽略這個字符串。
align:該參數決定了統一縮放的對齊方式,可以取下列值:
none - 不強制統一縮放,這樣圖形能完整填充整個viewport。
xMinYMin - 強制統一縮放,并且把viewBox中設置的
xMidYMin - 強制統一縮放,并且把vivewBox中X方向上的中點對齊到viewport的X方向中點處,簡言之就是X方向中點對齊,Y方向與上面相同。
xMaxYMin - 強制統一縮放,并且把viewBox中設置的
類似的還有其他類型的值:xMinYMid,xMidYMid,xMaxYMid,xMinYMax,xMidYMax,xMaxYMax。這些組合的含義與上面的幾種情況類似。
meetOrSlice:可選參數,可以去下列值:
meet - 默認值,統一縮放圖形,讓圖形全部顯示在viewport中。
slice - 統一縮放圖形,讓圖形充滿viewport,超出的部分被剪裁掉。
下圖詮釋了各種填充的效果:
用戶坐標系的變換 - transform屬性
該類型變換是通過設置元素的transform屬性來指定的。這里需要注意,transform屬性設置的元素的變換,只影響該元素及其子元素,與別的元素無關,不影響別的元素。
平移 - translate
平移變換把相關的坐標值平移到指定的位置,該變換需要傳入兩個軸上平移的量?蠢樱
這個例子繪制一個矩形,并把它的起點(0,0)平移到(30,40)處。雖然可以直接設置(x,y)的坐標值,但是使用平移變換去實現也很方便。這個變換第二個參數可以省略,默認當0處理。
旋轉 - rotate
旋轉一個元素也是一個很常見的任務,我們可以使用rotate變換實現,該變換需要傳入旋轉的角度參數?蠢樱
這個例子會顯示一個旋轉45度的矩形。有幾點注意:
1.這里的變換是以角度值為參數的。
2.旋轉指的是相對于x軸的旋轉。
3.旋轉是圍繞用戶坐標系的原點(0,0)展開的。
傾斜 - skew
transform還支持傾斜變換,可以是沿著x軸的(左右傾斜,正角度為向右傾斜,其實是傾斜了y軸),或者是沿著y軸的(上下傾斜,正角度為向下傾斜,其實是傾斜了x軸)傾斜;該變換需要傳入一個角度參數,這個角度參數會決定傾斜的角度?聪旅娴睦樱
從結果中,你可以直接看到同樣尺寸的矩形,在不同的傾斜變換后,得到的位置和形狀。這里注意矩形的起始位置都已經改變了,這是因為在新的坐標系統中,(30,30)已經在不同的位置了。
縮放 - scale
縮放對象由縮放變換完成,該變換接受2個參數,分別指定在水平和豎直上的縮放比例,如果第二個參數省略則與第一個參數取相同的值?聪旅娴睦樱
變換矩陣 - matrix
學過圖形學的都知道,所有的變換其實都是由矩陣表征的,所以上面的變換其實都可以用一個3*3矩陣去表示:
a c e b d f 0 0 1
由于只有6個值用到了,所以也簡寫成[a b c d e f]。把matrix(a,b,c,d,e,f)賦給transfrom就可以實施相應的變換。變換會把坐標和長度都轉換成新的尺寸。上面各種變換對應的矩陣如下:
平移變換[1 0 1 0 tx ty]:
1 0 tx 0 1 ty 0 0 1
縮放變換[sx 0 0 sy 0 0]:
sx 0 0 0 sy 0 0 0 1
旋轉變換[cos(a) sin(a) -sin(a) cos(a) 0 0]:
cos(a) -sin(a) 0 sin(a) cos(a) 0 0 0 1
沿X軸的傾斜[1 0 tan(a) 1 0 0]:
1 tan(a) 0 0 1 0 0 0 1
沿Y軸的傾斜[1 tan(a) 0 1 0 0]:
1 1 0 tan(a) 1 0 0 0 1
變換本質
前面我們總結canvas的時候,我們知道各種變換都是作用在用戶坐標系上的。在SVG中,所有的變換也都是針對兩個坐標系(本質上都是"用戶坐標系")的。當給容器對象或圖形對象指定"transform"屬性,或者給"svg,symbol,marker,pattern,view"指定"viewBox"屬性以后,SVG會根據當前的用戶坐標系統進行變換,去創建新的用戶坐標系,并作用于當前的對象以及它的子對象。該對象中指定的坐標和長度的單位不再是1:1的對應到外圍的坐標系,而是隨著變形,轉換到新的用戶坐標系中;這個新的用戶坐標系是只作用于當前的元素及其子元素。
變換鏈
transform屬性支持設置多個變換,這些變換只要中間用空格分開,然后一起放到屬性中就可以了。執行效果跟按順序獨立執行這些變換是一樣的。
上面的效果與下面的一樣:
單位
最后說一下單位,任何坐標和長度都可以帶和不帶單位。
不帶單位的情況
不帶單位的值被認為帶的是"用戶單位",就是當前用戶坐標系的單位值。
帶單位的情況
svg中相關單位與CSS中是一樣的:em,ex,px,pt,pc,cm,mm和in。長度還可以使用"%"。
相對度量單位:em和ex也與CSS中一樣,是相對于當前字體的font-size和x-height來說的。
絕對度量單位:一個px是等于一個"用戶單位"的,也就是"5px"與"5"是一樣的。但是一個px是不是對應一個像素,那就看有沒有進行過一些變換了。
其他的幾個單位基本都是px的倍數:1pt=1.25px,1pc=15px,1mm=3.543307px,1cm=35.43307px,1in=90px。
如果最外層的SVG元素的width和height沒有指定單位(也就是"用戶單位"),則這些值會被認為單位是px。
SVG重用與引用
組合 - g元素
g元素是一種容器,它組合一組相關的圖形元素成為一個整體;這樣,我們就可以對這個整體進行操作。這個元素通?梢院蚫esc和title元素配合使用,提供文檔的結構信息。結構良好的文檔通?勺x性和渲染效率都不錯?匆粋小例子:
1. xmlns="http://www.w3.org/2000/svg"表明了整個svg元素默認的命名空間是svg。這個在無歧義的時候可以省略。這里由于svg文檔是一個XML文檔,XML命名空間的相關規則這里都是適用的。例如可以給svg顯示的指定命名空間,給命名空間提供別名等。
2. g元素是可以嵌套的。
3. 組合起來的圖形元素就和單個的元素一樣,可以給id值,這樣,需要的時候(例如動畫和重用一組元素)只用引用這個id值就可以了。
4. 組合一組圖形元素可以統一設置這組元素的相關屬性(fill,stroke,transform等),這也是使用組合的一種場景。
模板 - symbol元素
symbol元素用于定義圖形模板(模板可以包含很多圖形),這個模板可以被use元素實例化。模板的功能與g元素很相似,都是提供一組圖形對象,但是也有一些區別。與g元素不同的地方是:
1.symbol元素本身是不會被渲染的,只有symbol模板的實例會被渲染。
2.symbol元素可以擁有屬性viewBox和preserveAspectRatio,這些允許symbol縮放圖形元素。
從渲染角度來說,與symbol元素相似的元素是marker(定義箭頭和標號)和pattern(定義顏色)元素;這些元素不會直接被渲染;他們的使用方式基本都是由use元素去實例化。正是這個原因,對于symbol來說,'display'屬性是沒有意義的。
下面這個修改過的代碼顯示了symbol的使用方式:
定義 - defs元素
SVG允許定義一組對象,然后重用這組對象(注意,不僅僅是圖形對象)。最常見的例子如定義漸變色,然后再其他的圖形對象中賦給fill屬性。漸變色定義的時候是不會渲染的,所以這類型的對象可以放到任何地方。重用對于圖形對象中也是經常存在的,而且我們也不希望定義的時候直接渲染,而是想在引用的地方渲染,這個可以用defs元素實現。
通常情況下,推薦的做法是:只要有可能,就把被引用的對象放到defs元素中。這些對象通常是:altGlyphDef,clipPath,cursor,filter, marker,mask,pattern,linearGradient,radialGradient,symbol和圖形對象等。把這些對象定義在defs元素中很容易理解,所以就提高了可訪問性。
其實作為容器對象的g元素、symbol元素、defs元素都不同程度上提供了重用的作用,只不過每個元素的特性可能少許不同:比如g元素是直接渲染的,symbol和defs不會直接渲染,symbol含有viewBox屬性,會創建新的視窗。
通常都會給在defs中定義的元素賦予id屬性,并在用到的地方直接使用。根據元素的不同,這些定義可以用到不同地方,比如下面的漸進色作為屬性來使用了:
圖形相關元素的定義可以用use元素鏈接到文檔。例如:
在這里請注意xlink名稱空間的使用。盡管大多數查看器沒有它也將正確顯示這一項,但為了保持一致,xlink名稱空間應該在<svg></svg>元素上定義。
引用 - use元素
任何svg, symbol, g, 單個的圖形元素和use元素本質上都可以作為模板對象被use元素引用(例如初始化)。use引用的圖形內容會在指定的位置渲染。與image元素不同,use元素不能引用整個文檔。
use元素也有x, y, width和height屬性,這些屬性可以省略,如果不省略的話,會將被引用的圖形內容坐標或長度映射到當前的用戶坐標空間來。
use元素的作用過程就相當于把被引用的對象深拷貝一份到獨立的非公開的DOM樹中;這棵樹的父節點是use元素。雖然是非公開的DOM節點,但是本質上還是DOM節點,所以被引用對象的所有屬性值、動畫、事件、CSS的相關設置等都會拷貝多來并都還是會起作用,而且這些節點也會繼承use元素和use祖先的相關屬性(注意引用元素是深拷貝,這些拷貝過來的元素與原來的元素已經無關系了,所以這里不會繼承被引用元素祖先節點的屬性),如果這些節點本身有相關(CSS)屬性,還會覆蓋繼承來的屬性,這些與普通的DOM節點是一致的,所以對use元素使用"visibility:hidden"時要小心,并不一定會起作用。但是由于這部分節點是非公開的,在DOM操作中,也只能看到use元素,所以也只能操作到use元素。
從視覺效果來看,use元素更像是占位符,渲染完成后的視覺效果就和直接用被引用對象渲染是一樣的:
use元素引用一個symbol元素
(1) 把use元素換成g元素;
(2) 把use的除x,y,width,height,xlink:href外的屬性全部移到g元素;
(3) 把use的x,y屬性變成translate(x,y),追加到g元素的transform屬性最后;
(4) 把引用的symbol元素換成svg元素,這個svg元素會顯式使用use元素的width和height屬性(use元素沒有這些屬性則是100%);
(5) 把引用的symbol元素的圖形內容深拷貝到替換的svg中。
use元素引用一個svg元素
(1) 把use元素換成g元素;
(2) 把use的除x,y,width,height,xlink:href外的屬性全部移到g元素;
(3) 把use的x,y屬性變成translate(x,y),追加到g元素的transform屬性最后;
(4) 把引用的svg元素包括內容拷貝過來,這個svg元素會顯式使用use元素的width和height屬性(use元素沒有這些屬性則使用原來的值);
其他情況
(1) 把use元素換成g元素;
(2) 把use的除x,y,width,height,xlink:href外的屬性全部移到g元素;
(3) 把use的x,y屬性變成translate(x,y),追加到g元素的transform屬性最后;
(4) 把引用元素拷貝過來;
看下面例子的視覺效果:
下面的圖和上面的圖外觀是一樣的: