理解SVG中的 viewport,viewBox, preserveAspectRatio

龍恩0707發表於2019-08-01

閱讀目錄

一:理解viewport

該屬性表示的是SVG可見區域的大小。或者也可以叫畫布的大小。就好比我們的電腦螢幕,我們只能看到我們電腦螢幕的可視區裡面的內容,但是看不到電腦螢幕之外的內容。比如如下程式碼:

<svg width="200" height="200" style="border: 1px solid red"></svg>

如上程式碼,我們定義了一個svg的畫布,寬度為200px,高度為200px,邊框的顏色為紅色,1畫素,實線。因此該程式碼的顯示效果在頁面上顯示如下:

如上程式碼,我們設定了svg的畫布大小為200px*200px, 如果沒有帶單位的話,該單位預設是 px(畫素)。當然也有其他單位:比如:

em: 相對於父元素的字型大小。
ex: 相對於小寫字母的 'x' 的高度(不常用)
px: 畫素(在支援css2的圖形系統中,每英寸為96畫素)。
pt: 點(1/72英寸)
pc: 12點(1/6英寸)。
cm: 釐米
mm: 毫米
in: 英寸

當然我們還可以指定svg元素的width和height為百分比,當我們的svg元素巢狀在一個div裡面去的話,那麼它的百分比是相對於外層的div元素的寬度和高度進行計算的。當然如果我們的svg元素為根元素的話,那麼它的百分比是相對於視窗的尺寸來計算的。比如如下程式碼:

<div style="width:400px;height:400px; border: 1px solid red; ">
  <svg width="20%" height="20%" style="border: 1px solid red;"></svg>
</div>

效果如下所示:

如上圖我們可以看到,我們的svg的寬度和高度為82px; 因為div元素的寬度和高度為400px; 20%的話,20% * 400 = 80px; 再加上2px的邊框,因此一共82px。

如果我們沒有給svg設定寬度和高度的話,它預設的寬度為300px,高度為150px; 如下程式碼:

<div style="width:400px;height:400px; border: 1px solid red; ">
  <svg style="border: 1px solid red;"></svg>
</div>

執行的效果如下所示:

理解預設使用者座標

在svg中有一個預設的座標系統,其中 水平座標(x座標)向右遞增的,垂直座標(y座標)是向下遞增的。原點座標是(0, 0).  該座標系統類似於我們數學幾何中的座標。

比如我們現在建立一個200px寬,200px高的視口,然後我們在裡面繪製一個矩形,該矩形左上角在座標(10, 10)的位置,該矩形的寬度為50px, 高度為 50px,基本程式碼如下:

<svg style="border: 1px solid red;" width="200" height="200">
  <rect x="10" y="10" width="50" height="50" style="stroke: black; fill:none;"></rect>
</svg>
<div style="width:100px;height:100px;margin-left:10px;background: red;"></div>

執行效果如下:

如上可以看到,我們在svg上繪製了一個小矩形,矩形的寬度為50px,高度也是為50px,然後該矩形的左上角在座標(10, 10)的位置,因此該座標點離右邊(x座標向右是遞增的)和下邊(y座標向下是遞增的)偏移10px;然後繪製了一個寬度為50px,和高度為50px的小矩形。我們也可以看到我們下面還有一個div元素,設定了 margin-left: 10px; 其實在我們的矩形內部設定了偏移10px,和我們的margin-left 是一個意思的。

二:理解viewBox

viewBox="x, y, w, h"; 該屬性的含義是可視區盒子,即畫布的可視區。

viewport 和 viewBox 分別有自己的座標系,預設情況下,該兩個座標系是重合的,即轉換關係是 1:1。

x: 指左上角的座標,y: 左上角的縱座標,w: 指寬度,h: 指高度

1. 畫布(viewport)、可視區(viewBox) 的寬度的高度相等情況

比如如下程式碼:

<div style="width:100%;display:inline-block;">
  <svg style="border: 1px solid red;" width="400" height="200" viewBox="0,0,400,200">
    <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
  </svg>
</div>

如上程式碼,svg的畫布大小,寬度為400px,高度為200px,然後使用viewBox屬性定義畫布的可視區的大小寬度也是400px,高度也是200px, 因此該兩個座標系是重合的,因此我們使用 rect 來建立矩形的話,定義寬度和高度分別為100px,x軸和y軸的偏移位置為10px; 效果如下所示:

2. 畫布不變,可視區的寬度減小的情況

比如如下程式碼,可視區的寬度減少100px, 如下程式碼所示:

<div style="width:100%;display:inline-block;">
  <svg style="border: 1px solid red;" width="400" height="200" viewBox="0,0,300,200">
    <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
  </svg>
</div>
<div style="border: 1px solid red;width:300px;height:200px; margin-left: 60px;"></div>

執行結果如下所示:

如上程式碼,畫布大小的寬度是400px,可視區的寬度變成300px; 因此為了使他們的中心點能重合的話,因此需要向右移動的距離 = 400 - 300 / 2 = 50px; 由於 rect 中的x偏移了10px,因此加起來就是偏移了60px了,可以把下面的div作為參照物即可看到。

如下示意圖所示解釋

下面我們繼續把可視區的寬度改為200px; 如下程式碼:

<div style="width:100%;display:inline-block;">
  <svg style="border: 1px solid red;" width="400" height="200" viewBox="0,0,200,200">
    <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
  </svg>
</div>
<div style="border: 1px solid red;width:200px;height:200px; margin-left: 110px;"></div>

然後我們執行結果如下所示:

可以看到,矩形需要偏移的距離 = 400-200/2 + 10 = 110px; 從下面的div中的margin-left 我們可以看到 偏移110px的時候,才能使 畫布(viewport)和可視區(viewBox)的中心點能夠重合。我們可以繼續看如下示意圖所示:

3. 畫布不變,可視區的高度減小的情況

如下程式碼:

<svg style="float:left;border: 1px solid red;" width="200" height="200" viewBox="0,0,200,100">
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="float:left; margin-left: 20px; border: 1px solid red;width:200px;height:100px;margin-top:60px"></div>

執行結果如下所示:

如上可以看到,矩形向下偏移的距離 = 200 - 100 / 2 + 10 = 60px; 因此我們可以看到我們的下面的div元素 margin-top:60px; 就可以對齊了。

4. 可視區不變,畫布寬度變小

如果可視區不變的話,畫布寬度減少的話,那麼矩形也要等比例縮小,比如如下程式碼:

<svg style="float:left;border: 1px solid red;" width="100" height="200" viewBox="0,0,200,200">
  <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="float:left; margin-left: 20px; border: 1px solid red;width:100px;height:200px;margin-top:50px"></div>

執行結果如下所示:

如上可以看到,矩形本來大小是 100px*100px,現在畫布的寬度改成100px了,那麼矩形也變成原來的一半了,至於向下移動距離的計算 = 200 - 100 / 2 = 50px;

5. 可視區不變,畫布的高度變小

可視區不變,畫布高度變小的話,那麼矩形也要等比例縮放;如下程式碼所示:

<div style="width:100%;display:inline-block;">
  <svg style="float:left;border: 1px solid red;" width="200" height="80" viewBox="0,0,200,200">
    <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
  </svg>
</div>
<div style="margin-top:10px;border: 1px solid red;width:200px;height:80px;margin-left:60px"></div>

如上程式碼;可視區的高度是畫布的 200/80 = 2.5倍, 因此矩形高度也變成原來的2.5倍分之1. 因此計算公司如下:

200     100 
-——  =  ----  = 40px
80       x

因此矩形的寬度和高度等比例縮放到 40px; 偏移的距離計算方式 = 200 - 80 / 2 = 60px; 因此向右偏移 60px 即可。

如下所示:

也就是說,如果畫布的高度變小的話,那麼偏移是向右的,如果畫布的寬度變小,那麼偏移是向下的。

6. 可視區寬度和高度大於畫布的寬度和高度

如下程式碼:

<svg style="float:left;border: 1px solid red;" width="45" height="135" viewBox="0,0,200,200">
  <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="float:left;margin-left:20px;border: 1px solid red;width:45px;height:135px;margin-top:46px"></div>

執行結果如下所示:

如果可視區寬度和高度大於畫布的寬度和高度的話,那麼 矩形的寬度和高度的計算方式以 寬度和高度最小的那個來等比例計算,什麼意思呢?我們如上的畫布的寬度是45px,高度是135px,那麼寬度小於高度,因此需要按照45px來計算,因此計算方式 =

200     100
---  =  ----
45       x 

最後 x = 22.5px 了,由於畫布的寬度小於高度,因此需要向下偏移,那麼偏移的距離計算方式 = 135 - 45 / 2 = 45px; 因此向下偏移45px即可,至於上面的div元素 margin-top 為46px,那是因為帶了一畫素邊框。

為了驗證這個邏輯,我們可以讓畫布的寬度大於高度,如下程式碼所示:

<svg style="border: 1px solid red;" width="145" height="43" viewBox="0,0,200,200">
  <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="border: 1px solid red;width:145px;height:43px;margin-top:10px; margin-left: 52px;"></div> 

執行效果如下所示:

矩形的大小計算方式  = 

 200   100
 --- = ---- = 21.5px
  43     x

也就是說 畫布的寬度和高度,那個小就根據那個來縮放比例,現在是高度為 43 x, 43px 小於 寬度 145px, 因此 按照高度 43px 來計算,因此最後我們的矩形的等比例縮放的大小為 21.5px; 那麼向右偏移的距離 = 145 - 43 / 2 = 51px 了,同理我們上面的div參考元素 margin-left: 52px, 那是因為邊框有1px;

但是如果如下程式碼:

<svg style="border: 1px solid red;" width="145" height="43" viewBox="0,0,200,80">
  <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="border: 1px solid red;width:145px;height:43px;margin-top:10px; margin-left: 19px;"></div>

執行效果如下了:

矩形的計算方式 = 

43      x
---  = ---- = 53.75px
80      100

然後偏移的距離 = 80 - 43 / 2 = 37 / 2 = 19px 左右。

三:理解 preserveAspectRatio

該屬性的作用是:它允許我們指定被縮放的影象相對視口的對齊方式。基本的使用方法如下所示:

preserveAspectRatio = "alignment [meet | slice]"

其中 alignment 指定軸和位置,預設值為 preserveAspectRatio = "xMidYMid meet";

preserveAspectRatio 該屬性是應用在SVG上,且和viewBox屬性配合一起使用的。viewBox屬性值可以指明是否可以等比例縮放(寬高比相同的情況下),以擴充套件到viewport指定的大小區域中。

該物件第一個引數有如下9個不同的值,分別為如下:

xMinYMin,
xMinYMid,
xMinYMax,

xMidYMin,
xMidYMid,
xMidYMax,

xMaxYMin,
xMaxYMid,
xMaxYMax

如上 x 和 y 表示對齊的軸線,x 表示水平方向物件(往右邊是正數),y表示縱向物件(往下是正數)。min, mid, max 表示對齊的方式。min 是往座標小的方向對齊,mid是居中對齊,max是往座標大的方向對齊。

第二個引數有3個值可選,分別為:meet 和 slice 和 none。

meet 的含義是:viewBox保持等比例縮放,整個viewBox在viewport中都是可見的。在滿足2個約束的條件基礎上,儘可能的放大viewBox,當viewport的寬高比和viewBox的寬高比不匹配的時候,那麼取寬高比中較小的那個。

slice 的含義是:修剪viewBox保持等比例縮放,整個viewport區域會被viewBox覆蓋。在滿足2個約束的條件基礎之上,儘可能的縮小viewBox,當viewport的寬高比和viewBox的寬高比不匹配時,取寬高縮放比中比較大的那個。

none 的含義是:不強制等比例縮放,儘量以viewBox和viewport以實際的寬高比來縮放圖形,儘量把寬度和高度擴充套件到這個viewport上。最後的結果就會使影象變模糊。

我們分別來看下demo:

1. preserveAspectRatio="xMinYMin meet"情況下

強制縮放比例,xMin:viewBox的x軸和viewport的x軸最左邊對齊,YMin:viewBox的y軸和viewport的y軸最左邊對齊。

如下程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMin meet"
>
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行效果如下:

如上我們看到,當我們設定 xMinYMin 的時候,意味著 x座標和y軸座標在原點上(0, 0)。

2. preserveAspectRatio="xMinYMid meet"情況下

強制縮放比例,xMin:viewBox的x軸和viewport的x軸最左邊對齊,YMid:viewBox的y軸和viewport的y軸中點對齊。

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMid meet"
>
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行效果如下:

示意圖如下:

注意: 如上 xMinYMid 和 yMidXMin 效果是不一樣的,最前面的優先順序最大,所以它會先以上面的 xMin最左端對齊。

3. preserveAspectRatio="xMinYMax meet"情況下

強制縮放比例,xMin:viewBox的x軸和viewport的x軸最左邊對齊,YMax:viewBox的y軸和viewport的y軸最下邊對齊

如下程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMax meet"
>
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行效果如下圖所示:

示意圖如下所示:

4. preserveAspectRatio="xMidYMin meet"情況下

強制縮放比例,xMid:viewBox的x軸的中心和viewport的x軸中心對齊,yMin: viewBox的Y軸最上方和viewport的y軸最上方對齊。

如下程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMidYMin meet"
>
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行效果如下圖所示:

示意圖如下所示:

5. preserveAspectRatio="xMidYMid meet"

強制等比例縮放,xMid:viewBox的x軸中點和viewport的x軸中點對齊,YMid: viewBox的y軸中點 和 viewport的y軸中點對齊。

如下程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMidYMid meet"
>
  <rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行結果如下所示:

如上程式碼,畫布大小的寬度是400px,可視區的寬度變成300px; 因此為了使他們的中心點能重合的話,因此需要向右移動的距離 = 400 - 300 / 2 = 50px; 由於 rect 中的x偏移了10px,因此加起來就是偏移了60px了,可以把下面的div作為參照物即可看到。

如下示意圖所示解釋

如上程式碼是我們之前分析的程式碼,因為畫布是400px,我們的可視區viewBox的寬度變成300px,但是我們的 preserveAspectRatio 的預設屬性值為:"xMidYMid meet"; 也就是說 x 軸方向是居中的,y軸方向是居中的,因此他們的中心點是相同的。

6. preserveAspectRatio="xMidYMax meet"

強制等比例縮放,xMid:viewBox的x軸中點和viewport的x軸中點對齊,YMax: viewBox的y軸最下方和 viewport的y軸最下方對齊。

程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMidYMax meet"
>
<rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

效果如下:

如下示意圖所示解釋

7. preserveAspectRatio="xMaxYMin meet"

強制等比例縮放,xMax:viewBox的x軸和viewport的x軸最右邊對齊,YMax: viewBox的y軸和 viewport的y軸最上方對齊。

程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMaxYMin meet"
>
<rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>
<div style="width:100%;display: inline-block;">
  <div style="margin-left:110px;border: 1px solid red;width:300px;height:200px;"></div>
</div>

效果如下:

如下示意圖所示解釋

如上程式碼;xMax是指 viewBox的x軸和viewport的x軸最右邊對齊,且 YMax: viewBox的y軸和 viewport的y軸最上方對齊。因此如上示意圖,如上面使用了div元素來做參考,margin-left:110px; 因為 畫布的大小是400px,我們的viewBox 是300px; 且rect元素向右移動了10px; 因此一共就是110px了。

8. preserveAspectRatio="xMaxYMid meet"

強制等比例縮放,xMax:viewBox的x軸和viewport的x軸最右邊對齊,YMax: viewBox的y軸和 viewport的y軸中心對齊。

效果和上面第七點是一樣的。這裡就不多解釋了。

9. preserveAspectRatio="xMaxYMax meet"

強制等比例縮放,xMax:viewBox的x軸和viewport的x軸最右邊對齊,YMax: viewBox的y軸和 viewport的y軸最下邊對齊。
效果和上面第七點是一樣的。這裡就不多解釋了。

10. preserveAspectRatio="xMinYmin slice"

slice 的含義是:修剪viewBox保持等比例縮放,整個viewport區域會被viewBox覆蓋。在滿足2個約束的條件基礎之上,儘可能的縮小viewBox,當viewport的寬高比和viewBox的寬高比不匹配時,取寬高縮放比中比較大的那個。

程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMin slice"
>
<rect x="10" y="10" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

效果如下:

如上我們可以看到我們的矩形的寬度和高度變為 133.33px * 133.33px了,為什麼呢?那是因為我們的viewport的寬度是400px,我們的viewBox是300px; 因此 400/300 = 1.33, 因此rect的寬度和高度的大小都需要在原來的寬度和高度都乘以1.33來鋪滿我們的viewport(畫布)的。

11 preserveAspectRatio="xMinYMid slice"

程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMid slice"
>
<rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

效果如下

示意圖如下所示:

如上所示:xMin 的含義是:viewport(畫布)的x軸與viewBox(可視區)的x軸的最左側對齊,YMid的含義是:viewport(畫布)的Y軸與viewBox(可視區)的Y軸的中心對齊。所以如上所示, 但是由於寬度和高度都等比例放大了,所以結果會向上偏移了。

12. preserveAspectRatio="xMinYMax slice"

程式碼:

<svg 
  style="border: 1px solid red;" 
  width="400" 
  height="200" 
  viewBox="0,0,300,200" 
  preserveAspectRatio="xMinYMax slice"
>
  <rect x="0" y="0" width="100" height="100" style="stroke: black; fill:none;"></rect>
</svg>

執行效果如下:

其他的略.....  感覺slice這個屬性沒有理解透。今天先到這....

相關文章