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
。另外,
width
andheight
不允许为负值,为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