什么是 viewBox
viewBox 表示了 svg 的可视区的范围,可以简单的理解为从 svg 中裁剪出来的某一块矩形区域内容,该内容是我们可见的区域,超出该区域的内容都被隐藏起来。在实际展示时,还会以着一定的比例,对该内容进行变换填充到实际的画布(容器)中去。viewBox 用(min-x,min-y,width,height)
表示,其含义是在 svg 空间中的 (x,y)
位置划出一个 width x height
的矩形区域。当没有指定 svg 的 viewBox 时,其默认值为 (0,0,当前容器的宽,当前容器的宽)
,实际渲染时无需进行任何转换。
html
<html>
<body style="display: flex">
<div style="margin: auto">
<svg width="300" height="150">
<rect width="100" height="100"></rect>
<rect x="300" width="100" height="100" fill="red"></rect>
</svg>
</div>
</body>
</html>
上图绘制红,黑两个矩形,但是其可见的区域为 300x150
的矩形,所以第二个矩形被隐藏起来了。当 viewBox 为 (100,0,300,150)后,红色矩形出现,同时黑色矩形消失。
html
<html>
<body style="display: flex">
<div style="margin: auto">
<svg width="300" height="150" viewBox="100 0 300 150">
<rect width="100" height="100"></rect>
<rect x="300" width="100" height="100" fill="red"></rect>
</svg>
</div>
</body>
</html>
此时从 svg 到实际容器的映射关系如下:
text
x1 = x - viewBox.x
y1= y - viewBox.y
因此原先的两个矩形坐标(0,0), (300,0)
分别映射为 (-100,0), (300, 0)
注意由于此时的 viewBox 的宽高和实际容器的宽高是一致的,所以没有发生伸缩现象,只是单纯的坐标平移,即上述简单的坐标平移
svg 的伸缩变换
当 viewBox 与实际容器的宽高不一致将会发生伸缩变换,此时需要根据preserveAspectRatio
属性来进行实际的坐标变换,没有指定preserveAspectRatio
时,默认采用 preserveAspectRatio="xMidYMid meet"
,先看最简单的一种情况,不进行伸缩即 preserveAspectRatio="none"
html
<html>
<body style="display: flex">
<div style="margin: auto">
<svg width="300" height="150" viewBox="50 0 150 300" preserveAspectRatio="none">
<rect width="100" height="100"></rect>
<rect x="300" width="100" height="100" fill="red"></rect>
</svg>
</div>
</body>
</html>
此时的变换关系如下:
text
x1=(container.width/viewBox.width)*(x-viewBox.x)
y1=(container.height/viewBox.height)*(y-viewBox.y)
以黑色矩形的坐标为例子(0,0), (100,0), (100,100),(0,100)
变换后的坐标为:(-100,0),(100,0),(100,50),(-100,50)
结果如下图所示,黑色矩形的长度变成了原来的两倍,但是其中一半被遮挡住
当preserveAspectRatio
不为 none
时,将会对 svg 的内容按 viewBox 的宽高比例进行伸缩,以适应容器的宽高。preserveAspectRatio
值形式如下preserveAspectRatio=[align meetOrSlice]
,第一个值为对齐方式,第二个值为伸缩方式。meet
的伸缩原则是保证伸缩后的元素完全在容器内部,而slice
则要求填充满容器,可能导致元素溢出 ,下面讨论默认情况下的变换关系,即preserveAspectRatio="xMidYMid meet"
,其余情况可以自行推导,过程是类似的。
text
1. 先对原图按照比例viewBox的比例进行伸缩,使内容完全显示在容器内
2. 根据 align 的类型进行平移
确定线性变化的比例
容器的宽高为 300*150
,viewBox 的宽高为150*300
,viewBox 超出容器,因此需要进行伸缩变换,其变化比例取各自长宽比的较小值
text
factorWidth =container.width / viewBox.width = 300/150=2
factorHight =container.height / viewBox.height = 150/300 = 0.5
因此 factor = 0.5
对 viewBox 内部的各个坐标进行线性变换则得到如下的关系
x1= factor*(x-viewBox.x)
y1=factor*(y-viewBox.y)
经过线性变换后的viewBox区域变成了 `75*150`的矩形区域
对于其内部各个点的变换结果如下(上述两个红黑矩形框为例):
原始点:
(0,0),(100,0),(100,100),(0,100)
(300,0),(400,0),(400,100),(300,100)
变换后:
(0,0),(50,0),(50,50),(0,50)
(150,0),(200,0),(200,50),(150,50)
由于设置了变换后的对齐方式为 `xMidYMid` 因此还需要对线性变换后的矩形进行平移,该对齐方式表示的是居中对齐, 即 viewBox 变换后的中点与容器的中点对齐,其变换关系如下:
平移的距离x = (container.width - viewBox.width*factor)/2 = (300-75)/2=112.5
平移的距离y = (container.height - viewBox.height*factor)/2 = (150-150)=0
最终的变换结果如下:
(112.5, 0),(162.5, 0),(162.5, 50),(112.5, 50)
(262.5, 0),(312.5, 0),(312.5, 50),(262.5, 50)
html
<html>
<body style="display: flex">
<div style="margin: auto">
<svg width="300" height="150" viewBox="0 0 150 300">
<rect width="100" height="100"></rect>
<rect x="300" width="100" height="100" fill="red"></rect>
</svg>
</div>
</body>
</html>