先来看一段简单的代码,我在 html 文件中直接使用 <svg>
标签进行绘制:
html
<!-- 代码片段 1.1 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
svg {
border: 1px solid red;
}
</style>
</head>
<body>
<svg width="300" height="200" viewBox="0 0 120 80">
<!-- 绘制一个边长为 100px 的正方形 -->
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
</body>
</html>
<svg>
标签内我定义了 3 个属性:
width
/height
:用于指定 svg 的宽高。注意,代码片段 1.1 中,如果指定了viewBox
但没有指定width
和height
:
html
<!-- 代码片段 1.1.1 -->
<body>
<svg viewBox="0 0 120 80">
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
</body>
则相当于只是指定了 svg 图形的长宽比,默认大小取决于该 svg 所在的 html 元素(代码片段 1.1.1 中即为 <body>
,svg 的宽将等于页面宽度);
viewBox
:简单地说是用来指定可见区域的。
代码片段 1.1 显示效果如下,红色边框为 svg 的画布区域:
得到的是一个宽高都超过了 100px 的矩形,而非边长为 100px 的正方形,其原因与 viewBox
属性有关,要想解释清楚,就需要先介绍下 svg 的坐标系统。
坐标系统
视口坐标系
同 canvas 类似,<svg>
元素也有一个坐标系统(网格系统),其以页面的左上角为原点 (0, 0),单位为 px
,x 轴方向朝右,y 轴方向朝下,称为视口坐标系:
(图片截自 MDN)
用户坐标系与 viewBox
svg 还有个用户坐标系,在初始时与视口坐标系相同,但可以通过 viewBox
进行修改,用于指定 svg 的可见区域。viewBox
的值有 4 个参数,min-x
、 min-y
、width
和 height
,前 2 个参数一般为 0
,不为 0
时则需要进行移动;后 2 个为可见区域的宽高。我通过 ps 给代码片段 1.1 画了个示意图如下:
蓝色矩形代表 svg 的视口坐标系,原点在 (0, 0),宽高分别为 300px 和 200px,宽高比为 3 : 2。红色矩形为通过 viewBox
修改后的用户坐标系,宽高分别为 120px 和 80px,宽高比同样为 3 : 2。黄色正方形则是在视口坐标系下绘制的边长为 100px 的正方形。可见看到,红色矩形在黄色正方形上截取了一块由白色虚线框起来的区域。
宽高比一致
代码片段 1.1 中,视口与用户坐标系的宽高比一致,并且 viewBox
的前 2 个参数为 0
,用户坐标系将拉伸占据整个视口:
如果遇上 viewBox
的前 2 个参数不为 0
的情况,比如都是 50
:
html
<!-- 代码片段 1.1.2 -->
<svg id="mySvg1" width="300" height="200" viewBox="50 50 120 80">
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
则用户坐标系的左上角就移动到 (50, 50),注意,对于 rect
的绘制还是从 (0, 0) 开始:
然后一样是将用户坐标系拉伸占据整个视口:
最终在浏览器呈现的效果如下:
宽高比不相等
viewBox 的前 2 个参数为 0
再来看一个视口和用户坐标系宽高比不相等的情况:
html
<!-- 代码片段 1.2 -->
<svg width="300" height="200" viewBox="0 0 200 100">
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
运行到浏览器的效果如下:
此时移动和缩放用户坐标系就与 <svg>
的另一个属性 preserveAspectRatio 有关了,其默认值为 xMidYMid meet
。这一点可以通过打印 svg 对象查看属性得知:
html
<!-- 代码片段 1.2.1 -->
<svg id="mySvg1" width="300" height="200" viewBox="0 0 200 100">
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
<svg
id="mySvg2"
width="300"
height="200"
viewBox="0 0 200 100"
preserveAspectRatio="xMidYMid meet"
>
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
<script>
window.onload = function () {
const s1 = document.getElementById('mySvg1')
const s2 = document.getElementById('mySvg2')
console.log(s1.preserveAspectRatio)
console.log(s2.preserveAspectRatio)
}
</script>
打印结果可以看到没有定义 preserveAspectRatio
属性的 svg 和定义了 preserveAspectRatio="xMidYMid meet"
的 svg 的打印结果是一样的:
并且通过查看原型上的属性,也能看到不同的值代表的含义:
xMidYMid
代表用户坐标系的 x 轴与 y 轴的中点要移动到与视口坐标系的 x 轴与 y 轴中点对齐,meet
代表缩放时要保持宽高比,直到有一个方向完全铺满视口坐标系,并且保证整个 viewBox(下图中的红色矩形 )在视图范围内可见(下图中的蓝色矩形):
viewBox 的前 2 个参数不为 0
如果视口和用户坐标系的宽高比不一样,并且 viewBox
的前 2 个参数也不为 0
:
html
<!-- 代码片段 1.2.2 -->
<svg width="300" height="200" viewBox="50 50 200 100">
<rect x="0" y="0" width="100" height="100"></rect>
</svg>
这种情况下,会先根据 viewBox
的后 2 个参数,也就是 width
和 height
,得到用户坐标系(左上角为 (0, 0))与截取的图形:
然后依旧是先让用户和视口坐标系的 x 轴 y 轴中心对齐,再进行保持宽高比例地拉伸,直到有一个方向铺满了视口坐标系(本例为 x 轴方向),最后按照拉伸的比例(本例为 svg
的宽 300 与 viewBox
的宽 200 之比,为 3 / 2),移动用户坐标系(本例为 50 * 3 / 2 = 75),当 min-x
与 min-y
为正数时,则代表向坐标轴的负方向移动:
代码片段 1.2.2 最终在浏览器呈现的效果如下: