此系列文章
不同於以往將 SVG 視為一張圖案(ICON 或 LOGO)的概念,在這篇文章中,我們要試著將 SVG 視為一個畫布(Canvas),而我們可以透過滑鼠來直接對這個畫布像 Google Map 一樣進行拖曳和縮放。
要將 SVG 視為一個畫布,並實做出縮放或拖曳的功能,有許多對於 SVG 的基本概念是我們需要先瞭解的,就讓我們一步一步來瞭解。
註1:在這篇文章中我們會把 SVG 視為一個畫布(Canvas),針對<svg></svg>而不是去探究 SVG 中各個元素(例如,<circle></circle>,<rect></rect>)。
註2:在這篇文章中我們只考慮 viewport 和 viewBox 為等比例的情況。
實做出來的效果會像這樣:
建議可以在 jsfiddle 中檢視,使用滑鼠縮放時比較不會拖曳到視窗。
瞭解 SVG 中的 Viewport 和 ViewBox
在 SVG 的世界中,空間的概念可以分成 viewport 和 viewBox 兩個部分。在這篇文章中,我會把它 Viewport 比喻作相框,ViewBox 比喻作相片。
Viewport 相對上比較好理解,就是相框的大小,也就是你的眼睛看得到的範圍,不管你的相片多大,你能看到的實際範圍就是相框的大小。在網頁中我們可以透過設定 viewport 來調整我們相框的大小。
ViweBox 則可以想成是這張照片的大小,如果相片的大小和相框(viewport)一樣大的時候,自然不會有什麼問題,你可以從相框中看到完整的相片。可是,如果相片(viewbox)比起相框來得大或來得小時,這時候就會比較麻煩些,你會需要多去控制這張相片應該要如何的排置在相框上,才能夠呈現出你想要呈現的東西,因此 ViewBox 中除了能夠控制的相片的大小之外,還能夠控制相片要如何擺放在相框中。
讓我們來瞭解一下 viewport 和 viewbox 的概念:
當 viewBox 等於 viewport 的情況(初始化的情況)
首先,我們可以直接在 SVG 元素上定義 viewport 的寬高,或者你也可以用 CSS 定義,基本上這個 DOM 元素的寬高就是 viewport 的寬高,也就是你相框的寬高。接著我們可以在 SVG 中定義我們的圖案內容,在這裡先用一隻鳥當作示範。
<svg id="svgElement" width="800" height="400">
<g><!-- 鳥的圖形 --></g>
</svg>
這時候便會得到如下的一個 viewport:再來我們可以設定 viewBox , viewBox 的設定可以寫在 SVG 的標籤上,設定屬性包含 <min-x> <min-y> <width> <height> 這四個屬性,也就是 viewBox = '<min-x> <min-y> <width> <height>' 。
我們剛剛有提到,ViewBox 中除了能夠控制的相片的大小之外,還能夠控制相片要如何擺放在相框中,其中 <min-x> 和 <min-y> 就是在控制相片要如何擺放在相框中,而 <width> 和 <height> 則是相片的大小。
預設的情況下(沒有特別去設定 viewBox 的情況下)viewBox 的大小會和 viewport 一樣 ,因此當我們把鳥的相片放入 svg 後,他會自動填滿整個相框,兩個的大小會是一樣的,程式碼會像這樣:
<svg id="svgElement" width="800" height="400" viewBox="0 0 800 400">
<g><!-- 鳥的圖形 --></g>
</svg>
相片會很完整的融入在相框當中當 viewbox 小於 viewport 時:圖案會被放大
透過 viewBox 的設定,我們可以進一步放大或縮小我們的相片,這裡有一個原則是,viewBox 會自動盡可能去填滿成 viewport 的大小,說起來很抽象,假設一開始我的 viewport 為 800 x 400 ,現在我們可以透過 viewBox 的設定,使得我們 viewBox 的大小變成 400 x 200,當我們的相片大小設定的比相框還小時,它會在原本的相片上裁切一小塊區域(在這裡是 400 x 200),接著把它調整到填滿整個 viewport 的大小(800 x 400)。分解過程像是這樣(綠色是 viewport,藍色是 viewBox)
(1)設定比 viewport 還要小的 viewBox:
我們在 800 x 400 的 viewport 中設定了 400 x 200 的 viewBox 。 |
這時候因為我們設定的尺寸比相片原本的大小還要小,所以相片會被裁切。 |
(3)填滿 viewport
最後 viewBox 會盡可能的填滿整個 viewport,造成放大的效果。 |
這時候就會產生一個很神奇的現象,因為我的相片尺寸(viewBox)設定的比原本相片的尺寸來的小,它會先把相片裁切成我們指定的大小,但是因為它會自動去填滿整個 viewport 的緣故,所以造成了相片被放大的效果。
程式碼寫起來像是這樣,設定為 viewBox="0 0 400 200":
<svg id="svgElement" width="800" height="400" viewBox="0 0 400 200">
<g><!-- 鳥的圖形 --></g>
</svg>
當 viewbox 大於 viewport 時:圖案會被縮小
類似的道理,當我們設定的 viewBox 大於 viewPort 時,它會先把這張照片的底圖放大(先放大成 1600 x 800),但是圖案大小不變,然後在盡可能的塞入 viewport 當中(這裡是 800 x 400):設定 viewBox 為 1600 x 800,比原本的 viewport 大,照片的底圖會被放大,但是圖案大小不會變。 |
把 viewBox (1600 x 800)塞進 viewport (800 x 400)當中,造成圖案變小的情況。 |
所以,雖然 viewBox 的設定(1600 x 800)大於原始 viewBox 的大小(800 x 400),但是實際上,照片在實際顯示上卻會被縮小。
程式碼寫起來會像這樣子,viewBox="0 0 1600 800":
<svg id="svgElement" width="800" height="400" viewBox="0 0 1600 800">
<g><!-- 鳥的圖形 --></g>
</svg>
SVG ViewBox 位置的設定
在 viewBox 的設定中,包含了四個屬性值 viewBox = '<min-x> <min-y> <width> <height>',除了上面我們所說的可以設定照片的大小外,也可以裡用 <min-x> 和 <min-y> 這兩個屬性來設定相片的位置。例如說當我設定 viewBox 的 min-x 為 150 時( viewBox = "150 0 800 400" ),viewBox 會向左移動 150 單位 :
將 viewBox 設定為 "150 0 800 400" 時,viewBox 會向左移動 150 單位。 |
實際上可以看到的圖案範圍變小。 |
我們也可以同時設定 min-x 和 min-y ,例如(viewBox = "-400 -200 800 400"),會得到這樣的效果:
將 viewBox 設定為 "-400 -200 800 400" |
我們最後實際上可以看到的圖案內容。 |
viewBox 設定造成的影響
在 viewBox 的設定的四個屬性值中 viewBox = "min-x min-y width height",我們可以簡單理解成前兩項 min-x 和 min-y 控制的是位移(translate),可以達到左右移動的效果;width 和 height 控制的則是縮放(scale)。
但是因為 viewBox 實際上影響的是 SVG 當中的座標系統(後面會說明 SVG 座標系統),所以會和你的直覺有寫相反,例如,當你設定 min-x 和 min-y 越大時,實際上看到的畫面會往左上方移動;同樣地,當你設定width 和 height 越大時,實際上看到的畫面會往縮小。
這個部分需要你花一些時間實際感受一下,非常建議利用下段中由 Sara Soueidan 所提供的實做案例感受。
實際感受 SVG 的效果
Sara Soueidan 在她所撰寫文章中 Understanding SVG Coordinate Systems and Transformations (Part 1) — The viewport, viewBox, and preserveAspectRatio 提供了非常好的實做案例。
建議先把 viewBox 設定成和 viewport 大小一樣(800 x 600),這是一般初始化狀況。 |
左上角的地方你可以去以用拖拉的方式去設定 viewBox 的值,以此感受 viewBox 的改變對於視覺上顯示的效果,另外,由於它的 viewport 是 (800 x 600) 所以建議你可以先把 viewBox 的長寬設為 800 x 600(也就是初始化時預設 viewport = viewBox 的狀況),接著再來實際操作看看 viewBox 的改變會有什麼樣的效果。
當 viewport 和 viewBox 的尺寸並不是等比例時:preserveAspectRatio
我們提到,當 viewBox 和 viewport 的尺寸大小不一樣時, viewBox 會盡可能的去填滿整個 viewport ,可是在上面文章的例子中,我們都把 viewBox 的尺寸大小設定的和 viewPort 是等比例的情況,如果是不同比例的話,又要用什麼樣的方式來對齊和填滿呢?
這時候我們就會需要用到 preserveAspectRatio這個屬性了。在這個屬性當中可以設定對齊的方式(align)還有要用什麼樣的方式填滿(meetOrSlice)。
在這篇文章中,我只打算探討 viewBox 和 viewport 為等比例的情況,我認為要瞭解 viewport 和 viewBox 的概念時,先以等比例的情況當作實例來練習是比較容易瞭解的,因為在等比例的情況下,preserveAspectRation 的值對於畫面的呈現是沒有任何影響的,如果對於 preserveAspectRatio 的屬性想要有更多的瞭解,可以參考這篇文章([譯] 理解 SVG 座標系統與 Transformations @ Andyyou),或進一步參考文章中最後面所列的參考文章。
深入瞭解 SVG 座標系
從上面我們可以看出當我們為 SVG 元素設定 viewBox 時,SVG 會有很多特別的效果,我們可以視整個 SVG 元素為一個畫布,對它進行縮放和移動,而實際上在 SVG 的世界中,我們要瞭解viewport 和 viewBox 實際上是處在兩個不同的座標系統中。
在 viewport 中,是我們過去所熟悉的,以瀏覽器中 DOM 為主的座標系統,通常把它稱作 viewport 座標系統(viewport coordinate system)或canvas 座標系統(但是不要把它和 HTML 中的 Canvas 標籤搞混)。一般來說,在這個座標系統中,1px 就是 1px 大,這個座標系統是相對固定的。在後面的文章中,我會使用 viewport 座標系統這個詞。
在 viewBox 的座標系統中,就不是我們所習慣的情況了,我們一般把在 viewBox 的座標系統稱作 SVG 座標系統(SVG coordinate system)、用戶座標系統(user coordinate system)(或 the current coordinate system、user space in use)。在後面的文章中,我會使用 SVG 座標系統這個詞。
在這個座標系統中,值是不一定要有單位的,如果我們沒有給它單位時,預設它會以 viewport 的單位為單位(例如,px),然而,
認識不同的座標名稱
為了幫助我們更進一步的探討 SVG 座標系統和 viewport 座標系統,我們要來認識三種不同的座標名稱,分別是 offset、client 和 SVG Point。
其中 offset 和 client 所取得的值都是屬於 viewport 座標系統中的座標值(也就是 1px 就是真實的 1px),兩者的差別在於 offset 是相對於 container 左上角的點,也就是以 container 左上角為(0, 0);而 client 則是相對於 window 左上角(瀏覽器視窗左上角)的點,以 window 左上角為(0, 0),越右 X 值越大,越下 Y 值越大。
offset 的座標值以 container 左上角為準;client 的座標值以瀏覽器視窗(window)左上角為準。 |
SVG 座標系統就比較特別一點了,它有專屬的座標點,可以透過 viewport 座標系的 client 座標加以轉換。
我們會在下一篇文章中說明如何取得這三個座標值,但目前你需要知道有這三種不同的座標名稱。
viewBox 等同於 viewport 時
我們用 Sara Soueidan 在所提供的互動案例來做更多說明,一開始的時候,我先把 viewBox 設成和 viewport 一樣大,在這種情況下 SVG 座標系統中的 1 單位大小會和 viewport 的一樣大,也就是 1px。留意藍色的尺標是 SVG 座標系統、灰色的尺標則是 viewport 座標系統:當 viewBox 等於 viewport 時,viewBox 就等於 viewport 的 1px,也就是實際 1px 大。 |
同時我們標下鳥右下角的這個點,讓我們後面能夠更清楚 SVG 座標系統的變化。在一開始的時候,因為 viewport 等同於 viewBox 的緣故,所以 SVG 座標系統中這個點,和 viewport 座標系統中的 offset 座標點,都會是(200, 300):
當 viewBox 為 viewport 尺寸的一半時
接著,我們可以看到,當我把 viewBox 設為 viewport 的一半(viewBox = "0 0 400 300"),也就是讓鳥看起來變 2 倍大時,這時候 SVG 座標系統中的 1 單位,會變成 viewport 的 2 單位(這裡就是 2px)。當 viewBox 為 viewport 的一半時,圖案會被裁切後放大,所以 SVG 座標系統中的 1 單位,會變成 viewport 的 2 單位大(2px)。 |
但要留意的是,雖然圖案被放大了兩倍,但是鳥右下角這點在藍色尺標的 SVG 座標系統中一樣是(200, 300),可是灰色尺標的 viewport 座標系統中的 offset 座標點會變成兩倍,也就是(400, 600)。
當 viewBox 為 viewport 尺寸的兩倍大時
同樣的道理,當我把 viewBox 設為 viewport 的 2 倍大(viewBox = "0 0 1600 800"),也就是讓鳥看起來變 2 倍大時,這時候 SVG 座標系統中的 1 單位,會變成 viewport 的 1/2 單位(這裡就是 0.5px)。這時候你會看到,雖然藍色尺標的 SVG 座標系統中,右下角座標仍然是(200, 300),但是 viewport 座標系統中的 offset 座標點則變成了(100, 150)。
重點總結
在這篇文章中有幾個你應該留意的重點:
- 知道有 viewport 和 SVG 這兩種不同的座標系統
- 知道有 offset, client, SVGPoint 這三種不同的座標點
- 知道有 offset 和 client 屬於 viewport 座標系統;SVGPoint 屬於 SVG 座標系統
- 知道透過 viewBox 的設定可以達到縮放和移動的效果
參考資料
- Understanding SVG Coordinate Systems and Transformations
- [中譯]理解 SVG 座標系統和 Transformation @ Andyyou
- SVG 研究之路 (23) - 理解 viewport 與 viewbox @ OXXO
- 首頁圖片來源 @ TeamTreehouse
0 意見:
張貼留言