快速了解vIEwBox的參數
vIEwBox屬性是用於指定用戶SVG圖像的坐標系統的原點以及尺寸的。所有在SVG內繪制的內容都是相對於這個坐標系統完成的。因為SVG畫布在所有方向都是無限延長的,你甚至可以在這個坐標系統的邊界之外的地方繪制圖形;但是這些相對於SVG視窗定位的圖形,也可以由用戶坐標系的位置來控制。
vIEwBox屬性使用四個參數來指定坐標系原點的位置以及它的尺寸:x y width height。初始情況下,這個坐標系等同於初始化的視窗坐標系(由SVG圖像的width和height確定),而且它的原點是在(0, 0)——即SVG的左上角。
通過改變x和y這兩個參數的值,可以調整原點的位置。改變width和height的值,可以改變坐標系統的尺寸。只使用vIEwBox屬性,就可以幫你擴展或裁剪SVG畫布。跟著示例一起閱讀。
重要提示:在本文章中,我不會改變viewBox在SVG視窗內的默認行為(比例和位置)。因為,根據屬性的默認行為,vIEwBox的內容會被盡可能地完全包含在視窗內,然後放置在中心位置。不過,使用preserveASPectratio屬性可以讓你自由地改變vIEwBox的尺寸和位置,但是在這篇文章中,這不是必需的技術,所以我們也不會在這裡深入講解。
使用viewBox裁剪SVG,即使用vIEwBox屬性制作Art Direction的SVG
前陣子,我的一個客戶要求把他網站的SVG頭像按照不同的屏幕尺寸設置成不同的大小,從而使得它只有一小部分是在小屏幕上可見的,在中等的屏幕尺寸上可以看到再大一點的部分,然後在大屏幕上可以看到完整的內容。當時我腦子裡首先出現的想法就是,他的要求其實是使用vIEwBox屬性來裁剪SVG圖像,然後根據不同的屏幕尺寸,顯示他想要看到的圖像的某部分。
通過改變SVG坐標系統的尺寸和原點位置,我們可以把SVG進行裁剪,並顯示我們希望在視窗中顯示的那部分內容。
我們來看看如何實現。
假設我們有如下這張完整的SVG圖像,然後我們想要把它裁剪成小屏幕的尺寸。這張圖是Freepik設計的可免費使用的房屋矢量圖,該圖片有Creative Commons Attribution 3.0 Unported協議的許可授權。為了簡單起見,我們先假設圖像只是要被裁剪成在中小屏幕上顯示的內容,以及大屏幕上顯示的完整的內容,如下所示。
左邊的圖的是我們將要使用vIEwBox屬性裁剪的完整的圖片,右邊的圖是我們希望在小屏幕上顯示的區域。
現在,通過改變vIEwBox屬性的值來裁剪SVG。有一些需要考慮的內容,我們等下會講到。但是首先,我們要改變坐標系統,讓它匹配上面的圖片中虛框矩形區域的內容。,通過調整系統的原點以及width和height的值,我們可以改變它初始的0 0 800 800參數值。
但是我們要如何獲知新坐標和新尺寸呢?重點是不要經過大量重復的實驗和錯誤。
有幾種方式。因為我們已經在圖形編輯器中(我的示例使用的是AI),我們可以使用編輯器的面板來獲取元素的位置和尺寸。
我畫這個虛線的矩形框,除了用來表示我想要在小屏幕上顯示的內容,還有一個原因就是:我們可以獲取這個矩形的位置和尺寸,然後把它們作為vIEwBox的值來使用。使用AI的變換面板(如下圖),我們獲取了我們需要的這些值。通過選擇矩形,並點擊右上角的變換鏈接,我們得到了如下圖所示的面板,包括我們所需要的x, y, width和height值。
這個AI中的變換面板可以用來獲取選中矩形的位置和尺寸的值
你可能注意到了,上面的值並不是整數,所以我們需要手動修改。根據上面的信息,我們把vIEwBox的值改成 0 200 512 512。
因為新的viewBox的寬高比和SVG視窗的寬高比是一樣的(都是正方形),viewBox內的內容將會擴大,並且只有被選中的區域會在視窗中顯示。改變vIEwBox的值之後,結果如圖:
新裁剪的SVG。只有我們指定使用vIEwBox屬性的位置在視窗中是可見的。藍色邊框表示SVG的視窗。
在這一點上,有一個問題需要解決:
如果被裁剪區域(即vIEwBox)的寬高比 != SVG視窗的寬高比?
在這種情況下,將會有明顯的溢出。明顯的溢出,我指的不是超出SVG視窗邊界的延伸,而是vIEwBox定義的相對於新用戶坐標系統的溢出。下圖作了相應的說明。
如果vIEwBox的寬高比和視窗的寬高比不同,SVG中的內容會溢出用戶坐標系統,結果可能會是這樣。
黑色邊框代表新的用戶坐標系,藍色邊框是SVG視窗。
上邊右圖中的黑色邊框是viewBox定義的區域。根據vIEwBox在視窗內的默認行為,它會被居中並盡可能放大,來保證自身內容盡可能地包含在視窗(藍色邊框)中。
因為SVG畫布從概念上來說,在所有方向都是無限延伸的,你可以在用戶坐標系統邊界之外繪制圖形,內容會直接溢出移動,如上圖所示。
如果你改變了SVG視窗的寬高比(SVG的width和height),來讓它們適應viewBox的寬高比,你就不會看到溢出了,因為vIEwBox的縮放是適應視窗的,和前面的示例一樣。
但是,在某些情況下,你可能不能或根本不想改變SVG的寬高比。比如說,如果你是使用SVG sprite作為一組圖像來顯示頁面上圖片的。在大多數情況下,圖像都有一個固定的寬高比——而且你並不想改變改變圖像的尺寸,就為了適應它裡面的某一張小圖的內容。或者可能你嵌入了一個圖標系統,並希望所有的圖標在同一個時間都保持相同的尺寸。
為了剪掉多余的東西(比如說,sprite上的部分其它圖標,在視窗中顯示),你可以使用<clipPath>來把多余的部分裁剪掉。裁剪路徑可以是一個覆蓋了整個vIEwBox區域的<rect>元素,然後將該元素應用到root SVG。
但是,還有一點要記住:確保<rect>的x和y屬性和vIEwBox的保持一致,除非rect將被相對定位於原來的/初始化的系統的原點,那麼SVG最終被裁剪的內容也是不確定的。
CSS Code復制內容到剪貼板當然,裁剪多余的部分也就意味著你仍然在使用不同的寬高比,還是需要解決內容兩邊多出的空白。如果SVG是一個連續的場景,像我們前面的那個例子,那就沒必要了,因為你還需要調整視窗的寬高比。如果SVG是一組圖標,並且你只是在不同的視窗中使用一次,這可能就不是問題了。
這裡有一點重要的東西要記住,vIEwBox的寬高比最好還是和視窗的寬高比保持一致;另外,你需要設置固定來避免SVG中任何不確定的多余的空白出現。
所以,vIEwBox可以用來裁剪SVG,並根據需要只展示SVG的某部分內容。但是它要如何應用於實例呢?
在響應式設計中的Art Directing SVG
這部分沒有什麼需要補充的內容,除了實際過程的代碼。所以,假設你有如上所示的SVG,並且想要把它作為頭像,比如說,在中小尺寸的屏幕上你只想顯示裁剪下來的那部分內容,然後在大屏幕上顯示完整的頭像。
改變SVG視窗的width和height值,我們可以使用CSS。但是改變vIEwBox的值,目前我們需要使用JavaScript。
並非所有的SVG屬性,CSS屬性都可以等同使用;只有一組具有和CSS屬性相同效果的屬性才可以在CSS中設置。你可以在這個表格中查看這組可以作為CSS屬性的SVG屬性的概述。在SVG2中,很多屬性(如x, y, cx, cy, r等等)都可以被添加到這個列表中;但是這些都是我們現在可以使用的屬性。
為了展示SVG的不同部分,需要基於不同媒體查詢改變vIEwBox的值,你可以使用Modernizr,查找媒體查詢條件,然後在JavaScript中對應更改vIEwBox的值。示例如下:
CSS Code復制內容到剪貼板這是可以運行的,但是如果我們可以使用CSS來完成這個效果豈不更贊?
使用CSS的vIEwBox屬性來裁剪SVG
免責聲明:在寫這篇文章的時候,並沒有CSSvIEwBox屬性。這只是一個用來解釋為什麼這個屬性有用,以及我想象的它如何被使用的示例。
理想情況下,我們可以這樣使用它:
CSS Code復制內容到剪貼板這些樣式會被放進(或取出)SVG中,然後SVG將會根據視窗的尺寸調整其vIEwBox值。讓它成為頁面視窗(內聯<svg>)的情況),或是通過其它任何引用SVG的元素的尺寸確定的視窗(這可以給我們一些近乎相同的元素查詢)。
不過,這在目前是不可能實現的,因為CSS中還沒有vIEwBox屬性。
前陣子,我就這個問題詢問了一個SVG規范的編輯,他說我可以根據實際使用情況和實例,向SVGWG提建議。後來在Twitter上進行了一些討論,我才發現在幾年前已經有一個相似的SVGWG proposal thread。當初的提議今天仍然存在,所以我希望,能夠通過一些實際使用示例,推進這個提議,並在不久的將來可以實現。如果你也想要在CSS中看到vIEwBox屬性,請幫助實現這一目標,推動這個提議的轉發和評論。
當使用vIEwBox完成SVG Art-Direction的時候,需要記住的東西
在做我的客戶的項目的時候,我花了一分鐘不到的時間來按照對方的要求對頭像進行art-direct。但是,這最終分出了三個獨立的SVG,而不是不同屏幕尺寸上的相同SVG不同vIEwBox。
我們選擇三個SVG的原因是,完整SVG的尺寸太大,在移動端達到了100kb以上的大小。最初的SVG是200kb左右的,我可以通過優化SVG來把文件壓縮到接近一半的大小,但是對於移動設備來說,圖片還是太大了,所以最後我們只能使用三張不同大小的圖像。art-directing SVG的時候,需要記住的就是:性能問題。如果你的SVG太大了,不要使用vIEwBox來art-direct。
現在,如果你選擇使用三個不同的SVG圖像,也有多種可能的方式可以完成——這取決於你嵌入SVG的方法,也取決於你想要完成什麼、不想完成什麼。
使用<picture>元素來完成不同SVG圖像是最理想的方式。它不僅能夠根據浏覽器為我們提供不同的可供選擇的SVG,而且還不需要使用JavaScript,還可以讓我們對不支持它的浏覽器(IE8及以下)提供多種優化的降級圖像。<picture>對於使用SVG是非常有用的,你可以在這篇文章中閱讀所有SVG fallback的內容。
但是如前面所說,如果你想要有動畫或交互效果的SVG,<picture>不是最佳選擇。就像使用<img>嵌入SVG,SVG不能被添加樣式和動畫,除非樣式和動畫是在<svg>文件中定義的,SVG不能添加腳本(出於安全考慮),也不能有任何交互(CSS或JS)——比如說,懸停,不會有交互效果。
所以,我總是說:SVG為我們提供了很多選項,可以讓我們完成幾乎所有的事情;你需要做一個權衡、要主次分明、有時甚至需要作出妥協,基於此作出最佳的選擇。但是對於性能,永不妥協才有利於發展!
在我們結束之前,因為我們提到了使用vIEwBox屬性改變SVG畫布尺寸的問題,我們來看看另一個例子,我們可以借助這個屬性來幫我們在處理SVG時節省一些時間和精力。
使用vIEwBox擴展SVG畫布
正如vIEwBox屬性可以用於縮放SVG,它也可以用來擴展SVG畫布。
幾周前我創建了一個可以生成SVG圓形菜單的工具。我創建了幾個例子來演示如何使用JavaScript讓生成的菜單動起來。demo使用<object>元素嵌入到應用程序頁面上。<object>的邊界定義了SVG視窗的邊界,任何在這些邊界之外的內容都是溢出,而且默認隱藏。
需要注意的是“邊界之外”指的是在SVG中的內容,它還是在無窮大的SVG畫布上的,但是超過了由視窗定義的無窮大的矩形
譯者注:關於SVG畫布、視窗的內容可閱讀w3cplus上的相關文章。
創建的菜單,SVG的尺寸是恰好可以將菜單包含進去,而沒有再大一些。避免了菜單周圍任何多余的空白。
我給某個菜單應用了一個彈跳動畫,作為菜單動畫的示例。這個彈跳效果“拉長了”各菜單項,也導致了菜單項在它們彈跳的時候會單獨切出來(即溢出)。
起初,由於SVG視窗是由<object>元素定義的,所以視窗和菜單本身是一樣大的,菜單項上的彈跳效果導致了這些菜單項在彈跳的時候溢出。
這些可愛的彈跳動畫應用於那些使用彈跳時間函數從0開始放大到100%的項目(即該項目最初是不可見的,縮小狀態),這個效果就是如果項目彈跳到超過了100%的大小,那就把它縮回到100%。這個效果會導致項目在彈跳超過SVG邊界的時候溢出。
下圖展示了縮放菜單項在放大到超過<object>的邊界(灰色邊框)的時候的效果,其中,<object>用於嵌入這個縮放菜單項。
上方的示意圖展示了菜單項放大到溢出SVG視窗邊界時的效果。灰色邊框表示SVG視窗的邊框(即<object>元素)。
給<object>設置overflow: visible也不能解決問題,因為<object>和<iframe>實際上是相似的。我們需要做的是擴展<object>創建的視窗內的SVG畫布,使得縮放的項目有足夠的“反彈”空間,而不會超過它的邊界。我們可以使用vIEwBox屬性來完成它。
為了延長SVG畫布,只需簡單增加它的尺寸。因此,我們使用的是700 x 500px的尺寸,而不是500 x 250這個SVG菜單的原始尺寸。這還會讓畫布在視窗中顯示的高度增加100px,而視窗中的畫布寬度會增加200px。我根據這些菜單項在彈跳效果需要放大的空間來確定這些值。根據您的SVG以及您要完成的具體內容,這些值並不要求一致。
現在,為了確保菜單是放置在視窗的中心的,我們需要把坐標系統的位置往負方向分別移動100px(即向上和向左)。把這個移動應用到坐標系統的原點上,和把一個平移轉換應用到系統中的菜單上是一樣的。結果是菜單會在視窗中保持居中。
在該圖中,藍色邊框表示SVG視窗邊界(即<object>元素),灰色邊框表示用戶坐標系統的初始尺寸。藍色數字和箭頭表示視窗中的坐標系統的擴展。
在延長用戶坐標系統尺寸的同時,你也增加了畫布在視窗中可見區域的面積。這樣做的結果是畫布的內容會顯得略小——這根據你把畫布放大了多少而定。但是對於菜單來說,這樣的結果是可以接受的。
下面的屏幕記錄顯示了擴展SVG畫布的結果,以及在SVG邊界內的菜單動畫效果。
一旦SVG畫布被擴展,菜單項就有足夠的空間來進行縮放,在應用彈跳效果的時候也不會再因為溢出被剪切。
通過改變vIEwBox屬性的四個參數值來延伸SVG畫布,這樣所有問題以及菜單項被剪切的問題都可以解決。vIEwBox確實非常棒~~
結束語
vIEwBox屬性非常棒,它就是一個SVG的加強版工具。通過使用這個屬性,在使用SVG進行工作時就可以節省很多時間,無需借助圖形編輯器即可快速解決SVG的問題。總而言之,這對於編輯SVG真的方便了很多。
我強烈建議你全面學習一下這個屬性,然後讓它在你的工作中發光發熱。如果你想要使用它來做art-direct SVG,不要忘了性能才是重點。