SVG奇淫巧技(一):viewBox和viewPort是个啥?
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章。
首先,抛出一个小问题,前几篇的演示案例中,<svg>标签总会出现一个viewBox属性,那么它是什么呢?想要弄清楚这个属性,就必须要了解另一个概念viewPort。
不卖关子,让我们带着这个问题开始今天的正文吧。
一、viewPort 和 viewBox
viewport 表示SVG可见区域的大小,其实就是<svg>标签占位的大小,同理于Canvas画布的大小。
<svg width="200" height="100"></svg>
上述代码就是定义了一个宽500单位,高300单位的一个视区。
注意:这里的措辞是
“单位”,不是“像素”。虽然说,width/height如果是纯数字,使用的就是“像素”作为单位的。
也就是说,上面SVG的视区大小就是 200px * 100px。
而之所以用“单位”来描述,是因为这里是不限制单位类型的,可使用的范围几乎涵盖常见的CSS单位: em 、px、%、in (英寸)、cm(厘米)、mm(毫米)、pt(1/72英寸)、ex(相对于小写字母‘x’的高度)等等。
<rect>的 width/height 与<svg>同理。
了解了可视区域的概念后,我们再来看下viewbox的定义:
MDN:
viewBox是一个包含 4 个参数的列表min-x,min-y,width和height, 以空格或者逗号分隔开, 在用户空间中指定一个矩形区域映射到给定的元素,查看属性preserveAspectRatio。另外,
widthandheight不允许为负值,为0则禁用元素的呈现。
绕来绕去的描述不如几行代码来的直观,我们直接上代码:

<svg width="200" height="100" viewBox="0,0,20,10">
<rect x="5" y="0" width="10" height="10" fill="red"/>
</svg>
可以看到,我们在一个200px * 100px的可视区域内,指定了一个viewBox="0,0,20,10"的矩形区域映射给<rect>。
根据MDN的定义我们将这里的viewBox分解一下:
min-x(左上角横坐标x): 0
min-y(左上角纵坐标y): 0
width(宽度): 20
height(高度): 10
此时,就得到了一个左上角为(0,0)点的 20*10 尺寸的矩形区域。不过,这时候又有了一个新的问题,为什么一个200px*100px的可视窗口,会被一个10px*10px的正方形填充了一半的面积呢?
这就是viewBox在大显神通了,毕竟10px*10px的正方形是被映射到了viewBox的管辖范围。
其实这个问题很好理解,你可以把viewBox想象成一个按照长宽比来缩放的舞台,通过下面这个动图应该会更容易理解。

viewbox在可视区域中根据自身长宽比例拉伸直至充满整个viewport视区。

所以,被映射到viewBox中的<rect>自然也被等比拉大了,这也就解释了,为什么200px*100px的视窗,会被一个10px*10px的正方形填充了一半的面积。
说道这里,不知道有没有小伙伴把viewBox看做成一个比例尺,无非是宽度分为20份,高度10份,映射区域的尺寸不是单位,而是占比的份数。
错,这是一个误区,最早我也是这么理解的,再解释为什么之前,请先记住:
viewBox不是比例尺,viewBox不是比例尺,viewBox不是比例尺!
好了摒弃这个误区之后,我们来试想一个问题,刚才的例子中200*100的viewport 和 20*10的viewBox,恰巧宽高比一致,那么如果宽高比不一致,SVG又会如何表现呢?

<svg width="200" height="100" viewBox="0,0,40,30">
<rect x="0" y="0" width="40" height="30" stroke="red" fill="none"/>
</svg>
我们看到,4:3的viewbox在2:1的viewport中2侧均有留白区域,并且居中,可是为什么会居中呢?
这就牵扯出另一个属性 preserveAspectRatio 了。
preserveAspectRatio 是应用在<svg>元素上,但作用对象却是viewBox,其默认值为:
preserveAspectRatio="xMidYMid meet"
preserveAspectRatio属性的值为空格分隔的两个值组合而成。例如,上面的xMidYMid和meet。
第1个值负责控制viewBox与viewport的对齐方式;
第2个值负责控制viewbox"撑满"viewport的方式。
我们来一个一个的了解,先看第1个值是如何表示对齐方式的?
首先这个值是由两部分组成,前半部分表示x方向对齐,后半部分表示y方向对齐(这里注意Y是大写)。除了默认xMidYMid 之外,还有其余的值:
| 值 | 对齐方式 |
|---|---|
xMin | 左对齐 |
xMid | 水平居中 |
xMax | 右对齐 |
YMin | 上对齐 |
YMid | 垂直居中 |
YMax | 下对齐 |
然后,从以上的值中选择你需要的x和y对齐方式之后,自由组合就好了,看起来和flex中的对齐方式十分类似不是吗?所以,上面的例子之所以居中,就是因为默认的xMidYMid让其垂直水平居中了,是不是很简单。
OK,到了最后一个知识点,preserveAspectRatio的第2个值,负责控制viewbox"撑满"viewport的方式,具体支持3个属性:
| 值 | 含义 |
|---|---|
meet | 保持宽高等比缩放viewbox来适应viewport |
slice | 保持宽高比,同时比例小的方向撑满viewport |
none | 不保持宽高比,充分撑满viewport |
看描述是不是有种似曾相识的感觉,没错,就如同我们电脑设置桌面时的平铺、裁剪和拉伸,结合案例来看就一目了然了:

巧用的preserveAspectRatio的不等比缩放
现在,关于 viewPort 和 viewBox的知识我们已经掌握了,当preserveAspectRatio的第二个值设为none的时候,SVG就失去了等比缩放的能力。
不知道大家有没有疑问,SVG的核心优势不就是等比缩放吗?那么什么场景下会用到非等比缩放的SVG呢?
这里也有个小误区,SVG的核心优势是矢量,而矢量与等比缩放无关,与放大后不失真有关。搞混的同学罚去看《SVG擅长些啥》。
话说回来,有些时候SVG的非等比缩放可能会产生意想不到的效果,例如:

上面的倒三角区域就是由空白处的SVG遮挡实现的,这里的SVG就是使用的非等比缩放,所以在不同的尺寸下,表现出的效果要比等比缩放的死板看起来自然的多。
二、利用SVG实现前端截图
一直以来,截图功能基本都是后端的专属,前端想要实现至少也要通过node.js来做到,但通过SVG中的foreignObject 元素再配合Canvas就可以轻松实现,不了解foreignObject的同学可以去《SVG不擅长些啥》补课,具体实现原理如下:
- 获取对应DOM元素的
outerHTML代码; - 放在
<foreignObject>元素中; - 用
<img>来显示我们的SVG;<img width="300" height="150" src='data:image/svg+xml;charset=utf-8, <svg xmlns="http://www.w3.org/2000/svg"> <foreignObject width="120" height="50"> <body xmlns="http://www.w3.org/1999/xhtml"> <p style="font-size:12px;margin:0;"> 一段需要word wrap的文字 </p> </body> </foreignObject> </svg>'> - 上一步的图片本质还是
SVG,我们可以借助canvas的drawImage()方法将图片放在画布上,然后使用canvas.toDataURL()方法转换成.png或.jpg图片,核心代码:var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.drawImage(img, 0, 0); img.src = canvas.toDataURL('image/png');
关于前端截图的代码和思路均来自于张鑫旭大佬,具体的实现方式和代码都在下方的参考链接里,大家可以去膜拜一下大佬的风采。
好了,今天就先到这里吧,下一篇我们来聊聊《SVG奇淫巧技(二):关于交互那点事》。
参考链接
转载自:https://juejin.cn/post/7139708499054821406