SVG 快速入門
原文鏈接: https://www.villianhr.com/2017/04/17/SVG 快速入門
SVG 全稱是 Scalable Vector Graphics,即,矢量圖。在 Web 中使用 SVG 可以解決點陣圖放大失真的問題。首先,不要把 SVG 和 CSS,Canvas,HTML 搞混。他們之間並沒有你中有我,我中有你的關係。SVG 是通過 XML 的形式寫在 HTML 文檔中的。
如何書寫
開篇說過,SVG 就是一個 XML。看一下代碼吧:
<svg x="0px" y="0px" width_="450px" height="100px" viewBox="0 0 450 100">n <rect x="10" y="5" fill="white" stroke="black" width_="90" height="90"/>n <circle fill="white" stroke="black" cx="170" cy="50" r="45"/>n <polygon fill="white" stroke="black" points="279,5 294,35 328,40 303,62n 309,94 279,79 258,94 254,62 230,39 263,35"/>n <line fill="none" stroke="black" x1="410" y1="95" x2="440" y2="6"/>n <line fill="none" stroke="black" x1="360" y1="6" x2="360" y2="95"/>n</svg>n
大家看 svg 標籤中帶有一個 viewBox 的屬性。這其實是 SVG 中一個很重要的概念,後面的縮放都會與它有關。
說到這裡,我們就需要了解一下關於 SVG 的幾個基本概念。
基本概念
簡單來說有 3 個基本概念:
- viewport: 物理窗口
- viewbox: 實物窗口(算了,下面解釋)
- preserveAspectRatio: 保留橫縱比
我們接下來,一個一個的進行講解吧。
viewport
參照上面的 demo,這實際上就是你用 x,y,width,height。這 4 個屬性,在頁面上固定的矩形區域。
viewbox
定義 SVG 元素在 viewport 中的具體尺寸比例。假設有如下內容:
<svg width_="500" height="200" viewBox="0 0 50 20" >n <rect x="20" y="10" width_="10" height="5"n stylex="stroke: #000000; fill:none;"/>n</svg>n
- viewport 為 [0,0] 到 [500,200]
- viewbox 為 [0,0] 到 [50,20]
默認情況下 SVG 是自動填充滿 viewport 的。注意,在 SVG 中,子標籤的所有尺寸都是不能帶單位的,因為初始單位就是根據上面兩個概念確定。
當為以上情況,SVG 中基本的尺寸則不是 1px,而是 500/50 = 10px。所以,如下的圖形大小為:
<rect x="20" y="10" width_="10" height="5"n stylex="stroke: #000000; fill:none;"/>n
也就是在 SVG 裡面定義的 rect 圖形,它的實際尺寸為 [200,100] 到 [100,50]。
preserveAspectRatio
該屬性就是用來定義上面 viewport 和 viewbox 相互對齊的方式。換句話就是說,它的屬性可以改變 viewbox 的具體位置。基本格式為:
<align> [<meetOrSlice>]n
- align: 定義 viewport 和 viewbox 的對齊方式,分為 x,y 軸兩個方向。X 軸方向有三種方式:左邊重合(xMin),x 軸中點重合(xMid),右邊重合(xMax)。同理,Y 軸也有 頂邊重合(YMin),y 軸中點重合(YMid),底邊邊重合(YMax)
- meetOrSlice: 主要就是定義該 SVG 是內嵌,還是裁剪或是 none(聽天有命)。
其中,align 需要著重理解一下。首先,它的默認值為 xMidYMid,即為中點重合。
可以從圖中看出,viewbox 是通過中心進行延展的。注意,它的原點坐標還是在 viewbox 的左上角。如果你是動態增加尺寸的話,此時並不是從左到右增加,而是從中心向兩端擴張。同理,如果你使用的是 xMinYMin 的話,那麼如果存在尺寸變化,那麼相對點則是從左上角開始的。簡單來說,align 相對點其實一共有 9 個。
然後就是 meet || slice || none 這三個屬性具體乾的事情。
在這之前,我們需要了解一個公式–縮放比計算公式:
vb_h * rat_y = vp_h; 或者 vb_w * rat_x = vp_w;
其中,vb_ 為 viewbox 簡寫,vp_ 為 viewport 的簡寫。vb_h 代表就是 viewbox height。vb_w 代表就是 viewbox width。rat_x/y 代表的是 x,y 軸的縮放比例。
假設有下列情況:
<svg width_="400" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin slice" stylex="border:1px solid #cd0000;">n <rect x="10" y="10" width_="150" height="150" fill="#cd0000"/>n</svg>n
那麼,rat_x 和 rat_y 分別為:
- rat_x = 400/200 = 2
- rat_y = 200/200 = 1
現在,針對上面 meet/slice 不同的取值,實際應用到 svg 裡面的縮放比例是不同的。
- meet(默認值): 本意是讓 svg 儘可能的顯示在 viewport 里,即,會在 rat_x 和 rat_y 中選擇最小的值作為縮放標準。
- slice: 本意是讓 svg 完全鋪滿 viewport,即,會在 rat_x 和 rat_y 中選擇最大的值作為縮放標準。
所以針對不同的取值,基準比例也不同。
當為 meet 的情況,那麼實際縮放比例為 1。則裡面實際矩形的大小就為 (10,10) 到 (150,150)。
當為 slice 的情況,那麼實際縮放比例為 2。則裡面實際矩形的大小就為 (20,20) 到 (300,300)。
如果你的值為 none 的話,他會直接鋪滿整個 viewport,即,實際矩形大小為:(20,10) 到 (300,150)。
響應式 SVG
雖然講起響應式,一些童鞋會想這 TM 又是啥奇技淫巧?
對不起,並不是。。。就是一個 viewbox 並且不帶 width/height 而已。
看個實際的例子吧:
<svg viewBox="0 0 218.8 87.1">n <g fill="none" stroke="#000">n <path d="M7.3 75L25.9 6.8s58.4-6.4 33.5 13-41.1 32.8-11.2 30.8h15.9v5.5s42.6n 18.8 0 20.6" />n <path d="M133.1 58.2s12.7-69.2 24.4-47.5c0 0 4.1 8.6 9.5.9 0 0 5-10 10.4.9 0n 0 12.2 32.6 13.6 43 0 0 39.8 5.4 15.8 15.4-13.2 5.5-53.8n 13.1-77.4 5.9.1 0-51.9-15.4 3.7-18.6z" />n</g> n</svg>n
可以看到,上面的 svg 標籤並沒有帶上啥 width/height 屬性,只是簡單描述了 viewBox 的範圍而已。當然,裡面的尺寸標準都是在這 viewBox 的範圍內進行設置的。
另外,在這裡聲明一下,本文章並不是新手教程,也就是說,不會教你一步一步的畫直線啊,圓啊,矩形啊等等這些基本圖形。這些直接 google 一下,一搜一大把。所以,這裡假設大家的水平是處於,能對 SVG 基本的圖形屬性熟悉即可,對一些高級屬性還不是很清楚和熟練。OK,繼續~
在 SVG 中,能夠直接使用的圖形有:
- rect
- circle
- ellipse
- line
- polyline
- polygon
上面沒有啥說的,後面我詳細說一下兩個比較重要的概念,分組和 Path。
分組和 Path
通常 Path 和 分組通常是一起使用的,如上:
<g fill="none" stroke="#000">n <path d="M7.3 75L25.9 6.8s58.4-6.4 33.5 13-41.1 32.8-11.2 30.8h15.9v5.5s42.6n 18.8 0 20.6" />n <path d="M133.1 58.2s12.7-69.2 24.4-47.5c0 0 4.1 8.6 9.5.9 0 0 5-10 10.4.9 0n 0 12.2 32.6 13.6 43 0 0 39.8 5.4 15.8 15.4-13.2 5.5-53.8n 13.1-77.4 5.9.1 0-51.9-15.4 3.7-18.6z" />n</g> n
分組我們放到後面進行講解,這裡先看一下 Path。
Path
Path 在 SVG 中的地位應該是比較高的,實際上,利用 Path 這個一個標籤可以畫出任意的圖形。path 中 d(data) 屬性是用來定義相關線條數據,通常是以 M/m 為起始,代表的就是 move to 的意思。在 path 中,一共可以定義 10 種不同的圖形。例如 M/m,L/l。 大家可以注意,每種標識符有兩種書寫方式,即,大小寫。
- 大寫: 參照的是絕對坐標,即,SVG 的右上角
- 小寫: 參照的相對坐標,即,前一個點的坐標。
而在 10 中不同表示符中,又可以分為直線和曲線兩種不同的標識符。這裡,我們分類來講解一下。
線型
M/m
該使用定義起始點的,沒啥特殊的作用。
<path d="M10 10"/>n
表示,以 (10,10) 為起始點。
L/l
原意是 Line to,用來畫線段的。格式和 M/m 差不多:
L x y (or l dx dy)n
H/h
用來畫水平線,即,Horizontal。既然方向已經定了,剩下的就是距離,格式很簡單:
H x (or h dx)n
V/v
用來畫豎直線,即,vertical。同上,方向也定了,格式為:
V y (or v dy)n
看個例子吧:
<path d="M10 10 H 90 V 90 H 10 L 10 10"/>n
該 path 實際上就是畫了一個正方形,寬 = 高 = 90。
Z/z
該標識符用來表示 path 的結束,並且將最後一點和 M/m 標識開頭的一點連接起來。所以,它不存在什麼表示點之類的,格式為:
Z (or z)n
而上面也可以進行相關的優化,最終的結果為:
<path d="M10 10 H 90 V 90 H 10 L 10 10"/>n// 使用 Zn<path d="M10 10 H 90 V 90 H 10 Z" fill="transparent" stroke="black"/>n
曲線
曲線就是 Web 畫圖中常見的 Bezier Curves(貝塞爾),Arcs,several Bezier curves(很多貝塞爾 - .-)等。
我們簡單看一下:
C/c
這是正統的貝塞爾曲線,需要 4 個參考點,下圖應該說比較確切表示了二次貝塞爾所需要的點。所以,C/c 需要定義三個點。
基本格式為:
C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)n
例如:
<path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>n
S/s
該標識符實際上使用來表示一個反射貝塞爾,即,在原有貝塞爾上再加一段貝塞爾曲線,所以,S/s 一般和 C/c 一起使用。
基本格式為:
S x2 y2, x y (or s dx2 dy2, dx dy)n
實際樣式圖為:
相當於原有的貝塞爾曲線的最後一段進行反向延長並對稱。然後加上新定義的一段限制曲線。
具體實例為:
<path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>n
Q/q
該標識符是用來定義二次(quadratic)貝塞爾曲線,該曲線相當於上面傳統的貝塞爾來說,更加簡單,它只需要定義三個點,即可完整一個貝塞爾曲線,具體作圖過程如下:
基本格式為:
Q x1 y1, x y (or q dx1 dy1, dx dy)n
即為圖上點, P1(x1,y1),P2(x,y)
起始點為 M 定義的點,例如:
<path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>n
T/t
該標識符和 S 差不多,也是一個貝塞爾曲線的延長。相當於原曲線的控制點 P1 相當於 end point P2 做對稱,然後,只需要定義一個終點即可,即,T/t 只需要定義貝塞爾曲線裡面的終點即可:
T x y (or t dx dy)n
如圖:
所以,簡單來說,C/S,Q/T 是兩兩搭配一起使用的。在使用的時候,千萬不要搞混即可。
弧線
A/a
該曲線是用來畫弧線(Arcs),而,弧線通常是圓/橢圓的一部分。當,橢圓的兩個軸徑長相等則為圓,所以,A/a 是按照橢圓作為基準格式:
A rx ry x-axis-rotation large-arc-flag sweep-flag x yn a rx ry x-axis-rotation large-arc-flag sweep-flag dx dyn
說實在的,這個比較複雜。因為,他畫橢圓的方式和我們平常不一樣,一般情況下,橢圓只要確定一個中心,然後是長短軸,然後是弧度範圍即可。
但是,它這裡是通過橢圓上的兩點來確定的,在加上旋轉角度,倆軸徑等因素來確定的。另外,需要注意,它的起始點是從上一個命令的結束點位置開始計算的。OK,我們首先簡單了解一下格式裡面的參數:
- rx,ry: 代表的就是長軸短軸,沒得說。
- x,y: 代表的是弧長的結束點。開始點就是上一個命令的終點。
- x-axis-rotation: x 軸的旋轉角度。順時針為正
- large-arc-flag[0,1]: 表示取大弧還是小弧。因為兩點之間的弧長有兩部分。
- sweep-flag[0,1]: 取順時針的弧,還是逆時針的弧長。參考點是以起始點開始的。
上面幾個屬性中,比較難理解的就是 large-arc-flag 和 sweep-flag。這麼說吧,前面幾個屬性充其量只能確定橢圓的位置,和經過橢圓的兩個點,不過,一般能通過指定兩點的橢圓有兩個,而通過這兩點劃分又會出現 4 段弧長。為了確定 4 個弧長中,是哪一個,需要兩個值來確定。即,4 抽 2,2 抽 1。
簡單說一種,例如當,laf 和 sf 都為 0的情況。首先,laf 為 0 選擇的是小弧長。所以,裡面兩段比較小的弧長被抽出來。然後,sf 為 0 選擇的是逆時針。即,以起始點為參考,選擇通過逆時針方向到達終點的那段弧。即,2 抽 1。最終得出我們需要的弧。
說實在的,這個是真 TM 複雜。。。
給一個參考 codepen。
一般情況下,我們並不需要手動來確定 path,有工具為啥不用工具呢!
比如,Illustrator, Sketch 等,都可以自動生成 SVG。不過,生成之後,需要對代碼做相關的壓縮優化,這些都可以直接在編譯器裡面找到。
你也可以用一下可視化工具 SVGOMG 來簡單看一下。
分組
SVG 中的分組你可以理解為 PS 中的圖層,一塊圖層裡面通常只會放一下高內聚的圖形,這樣既方便移動又方便做動畫。SVG 中的分組標籤就是 g,使用 g 標籤包裹的所有子元素都認同為一組。
例如:
<g>n <circle cx="20" cy="20" r="20" fill="green" />n <circle cx="70" cy="70" r="20" fill="purple" />n </g>n <g>n <rect x="110" y="110" height="30" width_="30" fill="blue" />n <rect x="160" y="160" height="30" width_="30" fill="red" />n </g>n
需要注意的是,使用 g 進行分組,並不會改變原有元素的在屏幕上展示的效果。
不過,g 標籤除了分組,還有另外一個很重要的功能–動畫
分組動畫
在分組重定義動畫是直接寫在 transform 屬性當中的。實際上,每個子標籤都可以使用 transform 的相關屬性。
<g transform="translate(...) scale(...) rotate(...) translate(...) rotate(...)">n ...n</g>n
每種變換動畫之間是通過 空格或逗號 連接的。它的執行順序是從右到左。為啥呢?實際上可以理解為,這就是幾個嵌套的 g 疊在一起。
<g transform="translate(...) scale(...) rotate(...) translate(...) rotate(...)">n ...n </g>n n // Being Equivalent to this:n <g transform="translate(...)">n <g transform="scale(...)">n <g transform="rotate(...)">n <g transform="translate(...)">n <g transform="rotate(...)">n ...n </g>n </g>n </g>n </g>n </g>n
具體可以使用的動畫形式和 CSS 動畫一模一樣,詳情可以參考: SVG 動畫
推薦閱讀:
※數據可視化第一步,SVG【小概念】
※SVG 圖標製作指南
※外刊君談中國第三屆CSS大會
※d3.js: 在曲線路徑上添加文本標記的正確方式