图解SVG的核心概念
在SVG中,对于很多初学者或者使用SVG有一段时间的同学来说,有几个概念总是会相互混淆。比如,SVG的画布、视窗(Viewport)、ViewBox等。这几个概念对于学习或掌握SVG来说是非常必要的。在此我们一起来学习和了解这几个概念。
画布
SVG和Canvas类似,它也有一个画布的概念。SVG中的画布是一个无限区域,即x
轴和y
轴都可以无限延伸。好比Sketch中,新创建的一个文件,你可以在新创建Sketch设计软件中的可见区域或不可见区域绘制任何图形(在Sketch可以通过拖跩让不见区域到可见区域):
如果回到SVG中的话,那么<svg>
标签就是一个画布,这个画布大小是无限的。
<svg></svg>
SVG的初始坐标
SVG也有一个初始坐标,这个坐标和我们所理解的Web坐标相似,都在屏幕的左上角。如果放到SVG画布中的话,这个坐标原点是在画布的正中间(即(0,0)
位置),沿着x
轴向右的水平方向为正值方向,沿着y
轴向下的方向为正值方向:
我们可以在这个坐标系统中(SVG画布中)任意位置绘制想要的图形,比如:
视窗(Viewport)
SVG画布是可以无限延伸的,但我们的设备屏幕本身并不是无限长的,于是SVG的渲染输出是必须对应有限尺寸的可视区域。这个可视区域将它称为视窗,即Viewport。这个可视区域指的就是SVG的显示范围。我们可以把Viewport想成浏览器视窗。
我们通过视窗来看里面的内容。
在SVG中,也可以把视窗当作一个容器,即创建了一个<svg>
标签就创建一个视窗。而这个视窗的大小可以通过<svg>
的width
和height
属性来指定大小:
<!-- 创建一个800 x 600 个单位的Viewport -->
<svg width="800" height="600">
<!-- SVG画布 -->
</svg>
注意,这里所说的一个单位指的是用户单位。比如上面示例中的800 x 600
并不是我们所说的px
单位,如果用户的坐标系统用的是cm
作为单位,那么对应的是800cm x 600cm
。
画布 vs 视窗
简单地说,如果你使用<svg>
标签就同时创建了一个SVG画布和一个视窗。这两个概念往往是很容易混淆的。因为它们两各自独立却又相互关联。因此,我们很有必要明白它们之间的关系。
为了更好的理解这两个抽象的概念,用生活中的一个实例来阐述。你可以把视窗想象成飞机上的窗户,把画布想象成窗外海阔天空的风景。坐在机舱的你只能透过窗户观看窗外的风景。
当然,SVG的画布和视窗并不是完全割裂的,它们之间有着一定的关联:
- 每创建一个
<svg>
元素,就相当于创建了一个无穷大的画布,同时创建了一个有限区域的视窗 - SVG的画布和视窗分别对应两个坐标系统,一个用户坐标系统(建立在SVG画布上的坐标系统),一个视窗坐标系统(建立在视窗上的坐标系统),这两个坐标系统默认是对齐的,它们的原点和坐标都是完全一致的,也就是说初始用户坐标系统的原点就位置视窗的左上角(也就是
x
轴正向向右,y
轴正向向下)
SVG的viewBox
SVG中还有另一个概念,那就是viewBox
的概念。同样拿生活中的示例来向大家阐述SVG中的viewBox
。在坐飞机的时候,你可能会用手机透过飞机的窗户(Viewport)来拍摄窗外风景(SVG画布),那么这个相机有一定的可视区域,这个区域就类似于SVG中的viewBox
:
拿着手机你可以在窗口区域(Viewport)内移动,这个时候只有手机区域(摄像机镜头)才可见,而且随着你移动,它的可见区域会变动。换到SVG的<svg>
标签中的话,它由四个参数来决定:
<svg viewBox="0 0 200 200">
</svg>
即viewBox
的值是:
viewBox = "<min-x> <min-y> width height"
其中<min-x>
和<min-y>
的值决定viewBox
的左上角顶点坐标,width
和height
则决定viewBox
的区域大小。
如果拿到Sketch中来的话,整个绘制工作区相当于SVG的画布,我们可一定的可视区域当作是SVG的视窗,而它的“画板”工具可以看作是SVG的viewBox
。只有在“画板”区域内的图形才能被看见:
SVG画布、视窗和viewBox
的关系
我们用一个示例来描述SVG世界中画布、视窗 和 **viewBox
**三者之间的关系。
简单地说,在SVG的世界中,底下有一张不限宽高的画布,好比上图中的浏览器可视区域,预设用px
当单位,我们在这个SVG画布中绘制图形;接着上面有一层SVG视窗(Viewport),好比上图中的相框;接着再有一层viewBox
,好比上图中的风景图(相片)。
正如前面提到的,SVG画布是无限大的(想象成上图中的浏览器可视区域可以不断扩展大小);SVG视窗是有一定大小的,比如上图中的相框大小(相框的宽和高决定了其大小),这也是你眼睛看得到的范围。也就是说,不管你的相片有多大(viewBox
),你能看到的实际范围就是相框的大小。
viewBox
则可以想成相片的大小,而这张相片可能:
- 相片可能会比相框小
- 相片可能会比相框大
- 相片可能会和相框一样大
如果相片和相框一样大的时候,你可以在相框中看到完整的相片。可相片和相框大小不同时,就会相对麻烦一些,我们要控制相片在相框中的位置和大小,才能在相框中呈现你想要呈现的东西。可以使用<svg>
的viewBox
的width
和height
来控制相片大小,使用<min-x>
和<min-y>
控制相片的位置。
视窗(相框)和viewBox
(相片)相等
我们来看一个示例:
<svg width="800" height="400" viewBox="0 0 800 400">
<image href="https://static.fedev.cn/sites/default/files/blogs/2020/2008/svg-in-react-32.png" width="800" height="400" />
</svg>
示例中的<svg>
的width
和height
创建了一个视窗(Viewport),它的大小是800 x 400
,也就是相框的宽高。而viewBox
属性创建了一个ViewBox。这个ViewBox除了能够控制相片大小之外,还能控制相片如何在相框中摆放,其中viewBox
的前两个值<min-x> <min-y>
(即0 0
)就是控制相片要如何摆放在相框中,而<width> <height>
则控制相片的大小。
预设的情况之下,ViewBox和Viewport大小一样,因此当我们把相片放到相框中,会自动填满相框:
视窗(相框)大于 viewBox
(相片)
我们可以通过<svg>
的viewBox
属性来调整ViewBox(相片)大小,这里有一个原则是,ViewBox会自动尽可能去填满整个Viewport。
<svg width="800" height="400" viewBox="0 0 400 200">
<image href="https://static.fedev.cn/sites/default/files/blogs/2020/2008/svg-in-react-32.png" width="800" height="400" />
</svg>
这个时候视窗大小是800 x 400
,而ViewBox的大小是400 x 200
。即ViewBox(相片)比视窗(相框)小,它会在原本相片上裁切一小块(ViewBox区域),接着把它调整到填满整个视窗。
我们用几张图来分解这个过程。在800 x 400
的视窗上设置了400 x 200
的ViewBox:
相片会按ViewBox的大小裁切:
最后ViewBox会尽可能的填满整个视窗,图片放大:
视窗(相框)小于 viewBox
(相片)
同样的使用viewBox
将ViewBox(相片)设置比视窗(相框)大:
<svg width="800" height="400" viewBox="0 0 1600 800">
<image href="https://static.fedev.cn/sites/default/files/blogs/2020/2008/svg-in-react-32.png" width="800" height="400" />
</svg>
这个时候viewBox
会让ViewBox(相片)变大(放大到1600 x 800
),但相片大小不变:
ViewBox比视窗大,但要让相片塞到相框中,相片就会缩小放到相框中:
SVG的ViewBox位置设定
上面的示例,我们看到的都是使用viewBox
来控制相片大小的。如果你想调整相片在相框中的位置,可以使用viewBox
中的另两个参数<min-x>
和<min-y>
,比如下面这个示例:
<svg width="800" height="400" viewBox="-100 -100 1600 800">
<image href="https://static.fedev.cn/sites/default/files/blogs/2020/2008/svg-in-react-32.png" width="800" height="400" />
</svg>
你将看到的效果如下:
从效果上可以看出,相片(ViewBox)的<min-x>
和<min-y>
设置的值都为-100
,ViewBox沿着x
轴向右移动了100
个单位,同时沿着y
轴向下移动了100
个单位。
再来看另外一个示例,<min-x>
和<min-y>
都是正值:
<svg width="800" height="400" viewBox="300 300 1600 800">
<image href="https://static.fedev.cn/sites/default/files/blogs/2020/2008/svg-in-react-32.png" width="800" height="400" />
</svg>
现在ViewBox(相片)有一部分移出了Viewport(相框)区域:
从这两个示例,我们可以发现:
- 当
<min-x>
的值为正值时,ViewBox会沿着x
轴向左移动;当其值为负值时,ViewBox会沿着x
轴向右移动 - 当
<min-y>
的值为正值时,ViewBox会沿着y
轴向上移动;当其值为负值时,ViewBox会沿着y
轴向下移动
你可能已经发现了,在viewBox
属性的四个参数中,其中<min-x>
相当于CSS中的translateX()
、<min-y>
相当于CSS的translateY()
,两者结合起来就类似于translate()
,主要用来对ViewBox进行移动;而width
和height
有点类似于CSS的scale()
,主要对ViewBox进行缩放。
由于viewBox
实际影响的是SVG当中的坐标系统,所以从直觉上来看的话,会有一种相反的感觉。当<min-x>
和<min-y>
的值越大时,ViewBox的越往左上方移动;同样的,当width
和height
越大时,看到的画面就小。
为了更好的让大家能理解SVG的Viewport和ViewBox之间的关系,我复制了@wattenberger的一个Demo,大家可以尝试着改变示例中的参数,查看viewBox
和视窗之间的变化:
SVG的preserveAspectRatio
前面花了一定的篇幅和大家一起探讨了ViewBox(相片)和 Viewport(相框)之间的关系,不难发现很多时候ViewBox和Viewport有不同大小的尺寸。在尺寸大小不一样时,SVG的ViewBox会尽可能的去填满整个Viewport,前面已经通过示例向大家演示过了。或许你已经发现了,前面阐述ViewBox和Viewport不同尺寸时两者关系的示例,有着一个共同特点,那就是ViewBox和Viewport的宽高比是等比的。
那么问题来了,如果ViewBox和Viewport的宽高比不是相同的,ViewBox(相片)又要用什么样的方式来对齐和填满Viewport(相框)?这个时候就需要用到<svg>
的另一个属性 preserveAspectRatio
。
接下来,我们来看一下preserveAspectRatio
如何使用,以及其作用。
W3C的规范是这样描述preserveAspectRatio
的:
preserveAspectRatio
属性表示是否强制进行统一缩放。适用于所有建立新SVG视窗(Viewport)的元素以及<image>
、<marker>
、<pattern>
和<view>
元素 。
需要特别提出的是,preserveAspectRatio
只适用于在相同元素(除了<image>
)上为viewBox
提供了值的情况。对于这些元素,如果没有提供属性viewBox
,则忽略preserveAspectRatio
。对于 <image>
元素, preserveAspectRatio
指示引用的图像应该如何与参考矩形进行匹配,以及是否应该相对于当前用户坐标系保留参考图像的长宽比。
我们再来看看preserveAspectRatio
属性的实际使用。preserveAspectRatio
属性接受两个值:
preserveAspectRatio = <align> <meetOrSlice>?
其初始值为**xMidYMid meet
**。其中:
<align> = none | xMinYMin | xMidYMin | xMaxYMin | xMinYMid | xMidYMid | xMaxYMid | xMinYMax |xMidYMax | xMaxYMax
<meetOrSlice> = meet | slice
<align>
参数指示是否强制统一缩放,如果是,当ViewBox(相片)宽高比和Viewport(相框)的宽高比不同时使用的对方方法。<meetOrSlice>
是可选的,用来指定图像缩放方式
在实际代码中,我们可以像下面这样使用:
<svg width="800" height="400" viewBox="0 0 400 200" preserveAspectRatio="xMidYMid">
<rect x="0" y="0" width="400" height="200" fill="#9090fa"></rect>
</svg>
<!-- 或者 -->
<svg width="800" height="400" viewBox="0 0 400 200" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="400" height="200" fill="#9090fa"></rect>
</svg>
对于很多同学,可能更关注的是<align>
和<meetOrSlice>
对应参数具体解释和所起的作用。
<align>
preserveAspectRatio
属性的第一个参数<align>
的值除了none
之外的每个值都是有由两个部分组成,即x
轴和y
的对齐方式的组合。比如xMidYMid
就是xMid + YMid
,其中xMid
用来描述ViewBox(相片)在Viewport(相框)中x
轴方向的对齐方式,YMid
是用来描述ViewBox(相片)在Viewport(相框)中y
轴方向的对齐方式。x
轴和y
轴每个方向都有三个值,具体代表的意思如下:
x 轴的值 |
含义 | y 轴的值 |
含义 |
---|---|---|---|
xMin |
ViewBox和Viewport左边缘对齐(水平居左) | YMin |
ViewBox和Viewport顶部边缘对齐(垂直居上) |
xMid |
ViewBox和Viewport在x 轴中心点对齐(水平居中) |
YMid |
ViewBox和Viewport在y 轴中心点对齐(垂直居中) |
xMax |
ViewBox和Viewport右边缘对齐(水平居右) | YMax |
ViewBox和Viewport底部边缘对齐(垂直居下) |
我们用张图来描述:
有一点需要声明:
preserveAspectRatio
的第一个参数的值是x
和y
轴组合物,因此在实际使用的时候,x
和y
轴的独立物是不存在的。即preserveAspectRatio="xMin"
或preserveAspectRatio="YMin"
是无效值。
也就是说,preserveAspectRatio
第一个参数<align>
的值是通过x
(三种)和y
(三种)可以组合出九个不同的值。
为了更好的理解<align>
中值的含义,这里上一张图,用来描述ViewBox和Viewport之间的几个轴的关系:
有了上图之后,<align>
中的描述会更易于理解一些:
<align> 值 |
含义 | 备注 |
---|---|---|
xMinYMin |
ViewBox的min-x 和Viewport的x 轴最小值对齐;ViewBox的min-y 和Viewport的y 轴的最小值对齐 |
表现行为类似于CSS中background-position: 0% 0% |
xMidYMin |
ViewBox的mid-x 和Viewport的x 轴中间点对齐;ViewBox的min-y 和Viewport的y 轴的最小值对齐 |
表现行为类似于CSS中background-position: 50% 0% |
xMaxYMin |
ViewBox的min-x + width 和Viewport的x 轴的最大值对齐;ViewBox的min-y 和Viewport的y 轴的最小值对齐 |
表现行为类似于CSS中background-position: 100% 0% |
xMinYMid |
ViewBox的min-x 和Viewport的x 轴的最小值对齐;ViewBox的mid-y 和Viewport的y 轴的中间点对齐 |
表现行为类似于CSS中background-position: 0% 50% |
xMidYMid |
ViewBox的mid-x 和Viewport的x 轴的中间点对齐;ViewBox的mid-y 和Viewport的y 轴的中间点对齐 |
表现行为类似于CSS中background-position: 50% 50% |
xMaxYMid |
ViewBox的min-x + width 和Viewport的x 轴的最大值对齐;ViewBox的mid-y 和Viewport的y 轴的中间点对齐 |
表现行为类似于CSS中background-position: 100% 50% |
xMinYMax |
ViewBox的min-x 和Viewport的x 轴的最小值对齐;ViewBox的min-y + height 和Viewport的y 轴的最大值对齐 |
表现行为类似于CSS中background-position: 0% 100% |
xMidYMax |
ViewBox的mid-x 和Viewport的x 轴的中间点对齐;ViewBox的min-y + height 和Viewport的y 轴的最大值对齐 |
表现行为类似于CSS中background-position: 50% 100% |
xMaxYMax |
ViewBox的min-x + width 和Viewport的x 轴的最大值对齐;ViewBox的min-y + height 和Viewport的y 轴的最大值对齐 |
表现行为类似于CSS中background-position: 100% 100% |
最后用一张图向大家展示<align>
不同值的效果:
<meetOrSlice>
<meetOrSlice>
是preserveAspectRatio
属性的第二个值,它主要由meet
和slice
两个值。其主要作用是:
控制ViewBox是否要在Viewport中完整展示。
不管是meet
还是slice
他有一个共同原则:ViewBox需要保持宽高比进行缩放。为了更好的讲清楚meet
和slice
,我有必须花一点点时间来阐述一下ViewBox宽高比的概念。比如下面这样的一个示例:
<svg width="800" height="400" viewBox="0 0 400 200">
</svg>
从示例中的viewBox
的值我们可以得知,ViewBox的width
是400
个单位,height
是200
个单位,这个时候其宽高比是2
:
r = width / height = 400 / 200 = 2
如果我们对ViewBox进行缩放,比如说, ViewBox放大到宽度为600
,高度为300
,此时ViewBox的宽高比也是2
,那么它就是属于一个保持宽高比的放大。如果ViewBox放大到宽度为600
,高度为200
,此时ViewBox的宽高比就变成了3
,那么它就不属于保持宽高比的放大。
有了这个概念之后,我们来看meet
和slice
是如何让ViewBox缩放。
meet
meet
是<meetOrSlice>
的默认值,ViewBox将缩放到:
- ViewBox保持宽高比进行缩放
- 整个ViewBox在Viewport内可见
- 尽可能放大ViewBox,同时仍然也满足其他的条件
我们用示例来描述meet
。
正如上图所示,ViewBox的宽高比是2
(400 ÷ 200 = 2
),并且将ViewBox放到两个不同的Viewport中(第一个是宽大于高,第二个是高大于宽),然后将ViewBox分别在不同的Viewport中进行缩放,分别做了三个不同的缩放处理:
- “
1
”号位ViewBox没有按宽高比进行缩放,不符合条件 - “
2
”号位ViewBox虽然按宽高比进行缩放,但溢出了Viewport,也不符合条件 - “
3
”号位ViewBox才是符合meet
,既符合按照ViewBox的宽高比进行缩放,而且整个ViewBox在Viewport中可见
slice
我们接着来看<meetOrSlice>
的第二个参数值slice
。该值会将ViewBox缩放到:
- ViewBox保持宽高比进行缩放
- 整个Viewport将覆盖ViewBox
- ViewBox将会被尽可能的缩小,但是仍然符合其他标准
同样用一个简单的示例来描述slice
:
正如上图所示,ViewBox的宽高比是2
(400 ÷ 200 = 2
),并且将ViewBox放到两个不同的Viewport中(第一个是宽大于高,第二个是高大于宽),然后将ViewBox分别在不同的Viewport中进行缩放,分别做了三个不同的缩放处理:
- “
1
”号位ViewBox没有按宽高比进行缩放,不符合条件 - “
2
”号位ViewBox虽然按宽高比进行缩放,但未填满整个Viewport,也不符合条件 - “
3
”号位ViewBox才是符合slice
,既符合按照ViewBox的宽高比进行缩放,而且ViewBox填满了整个Viewport
你是不是也发现了,<meetOrSlice>
的渲染行为和CSS中的background-size
取值为contain
和cover
非常的类似,其中meet
的行为类似于background-size: contain
,slice
的行为类似于background-size: cover
。
结合前面的介绍,<svg>
的preserveAspectRatio
属性中的<align>
不是none
(取值为none
时<meetOrSlice>
自动失效),那么preserveAspectRatio
的呈现行为就和background-position / background-size
类似。
阅读到这里,你应该对preserveAspectRatio
有一定的了解:
preserveAspectRatio
的<align>
和<meetOrSlice>
可以设定ViewBox在Viewport中的缩放与定位行为。可以决定ViewBox在Viewport中全部显示还是只显示局部!
在这里,我复制了@Sara Soueidan的一个Demo,大家可以通过改变<svg>
的viewBox
属性的各个参数的值和调整<svg>
的preserveAspectRatio
属性的参数值,来查看具体的效果:
虽然说preserveAspectRatio
的取值可以用来设置ViewBox在Viewport的位置和缩放方式,但有的时候也取决于viewBox
的width
和height
。比如,我们在上面的Demo上,将viewBox
的width
和height
分别调整到200
和 300
,并且<min-x>
和<min-y>
设置为0
,同时将preserveAspectRatio
的<meetOrSlice>
设置为meet
:
然后去调整preserveAspectRatio
的<align>
的值,将会发现不同的值也会达到一样的结果:
如果我们把<meetOrSlice>
的值换成slice
,结果就完全不同了:
因为slice
设定下,ViewBox要保持固定宽高比例(200 ÷ 300 = 0.6667
),ViewBox要填满整个Viewport的区域(800 x 600
),因此ViewBox在x
轴从200
个单位被换算到了Viewport的800
个单位(也可以简单的理解,ViewBox水平方向从200
放大到800
),这个时候ViewBox的width
就是800
个单位。为了满足slice
的第一个标准,ViewBox缩放需要保持宽高比,那么这个时候ViewBox的height
就需要从300
个单位换算到1200
个单位(800 ÷ height = 200 ÷ 300
,换算出来height = 1200
)。那么ViewBox的height
就比Viewport的height
更大,ViewBox将溢出Viewport,而且溢出的部分将被裁切。
如果你想体验
viewBox
和preserveAspectRatio
给SVG带来的不同变化,可以尝试着在上面的Demo上调整不同的参数值,查看相关的渲染结果!
深入了解SVG坐标系
从上面的内容我们可以看出,当<svg>
元素显式设置viewBox
属性时,SVG将会有很多特别的效果。比如SVG元素的移动,缩放,扭曲等。这主要是,有了viewBox
之后,SVG中的坐标系统也会随之改变。
前面我们提到过:SVG画布使用的是用户坐标,SVG视窗(Viewport)使用的是视窗坐标,而且视窗坐标只是用来作最后渲染的参考换算。新增的viewBox
却可以用来调整用户坐标(也可以将其理解为最终坐标系)。在SVG中,viewBox
的坐标系统可以比视窗坐标大,也可以比视窗坐标小。
我们也可以这样来理解,在SVG中通过在<svg>
标签上设置width
和height
的值,来指定视窗坐标,一旦视窗坐标系统初始化,浏览器预设会建立一个用户坐标系统(SVG画布坐标系统),其所有定义跟视窗坐标一样(SVG画布坐标系统和视窗坐标系统相同)。
后续我们可以使用<svg>
的viewBox
属性来调整用户坐标系统,如果用户坐标系统的宽高比和视窗一致,就会自动扩展以填满视窗的空间。如果用户坐标系统和视窗比例不同,就需要使用<svg>
中的另一个属性preserveAspectRatio
属性来调整“用户坐标系统”在视窗中显式方式。
也就是说,在SVG世界中,掌握ViewBox和Viewport,其实就是在处理这两个坐标系统!为了更好的帮助大家深入了解这两个坐标系统,依旧借助@Sara Soueidan的Demo渲染结果来向大家呈现。
先上图:
为了更好的呈现,将<svg>
的viewBox
的<min-x>
和<min-y>
的值调整为-100
个单位,其对应的代码就是:
<svg width="800" height="800" viewBox="-100 -100 800 600">
</svg>
在图中可以看到两个坐标系统,一个是灰色的(对应的是Viewport坐标系统),一个是蓝色的(对应的是ViewBox坐标系统)。
- Viewport坐标系也称为视窗坐标系,在这个坐标系统中,
1px
就是我们熟悉的1px
,而且这个坐标系统是相对固定的 - ViewBox坐标系也称为SVG坐标系或用户坐标系,在这个坐标系统中,值不一定是有单位的,如果没有显式指定单位,预设单位会以Viewport的单位为单位(例如
px
)
其中ViewBox坐标系和Viewport坐标最大的区别是:ViewBox坐标系统中1
单位大小不一定是1px
。我们来简单地看几个这方面的示例。
假设ViewBox和Viewport大小等同:
<svg width="800" height="600" viewBox="0 0 800 600">
</svg>
这个时候,ViewBox坐标系统中的1
单位大小会和Viewport坐标系统中的一样大,即**1px
**。比如下图所示,留意蓝色和灰色的尺标:
为了更好的向大家说清楚ViewBox坐标系统的变化,可以把上图中鸟的右下角标注出来:
接着,把viewBox
的值调整为viewBox="0 0 400 300"
(ViewBox相当于Viewport的一半):
从上图中可以发现那只鸟看起来放大了两倍,蓝色的尺标也看上去放大了两倍。鸟的右下角那个点也变了:
鸟右下角顶点坐标,在ViewBox坐标系统(蓝色标尺)对应的还是(200, 300)
,但在Viewport坐标系统(灰色标尺)对应的就变成了(400, 600)
。这个时候,ViewBox坐标系统中的1
单位对应的就是Viewport坐标系统的2
单位(在这个示例中就是2px
)。
再来看另一个场景,将viewBox
属性的值调整为viewBox="0 0 1600 1200"
(ViewBox相当于Viewport的两倍):
从上图中可以发现那只鸟看起来缩小一半,蓝色的尺标也看上去缩小了一半。鸟的右下角那个点也变了:
鸟右下角顶点坐标,在ViewBox坐标系统(蓝色标尺)对应的还是(200, 300)
,但在Viewport坐标系统(灰色标尺)对应的就变成了(100, 150)
。这个时候,ViewBox坐标系统中的1
单位对应的就是Viewport坐标系统的0.5
单位(在这个示例中就是0.5px
)。
上面三个示例中的viewBox
的<min-x>
和<min-y>
都是0
。经过前面的学习,viewBox
的值改变对于ViewBox系统的变化是多样的。比如,viewBox
的值从0 0 800 600
换成-100 -100 800 600
,ViewBox坐标系统和Viewport坐标系统就不同:
从上图中可以看出,ViewBox坐标系统原点从(0, 0)
移到了(-100, -100)
(对应Viewport坐标系统中的(100, 100)
),看上去整个ViewBox坐标系统在Viewport系统中做了一个位移(向右移了100
,向下移了100
)。由于ViewBox和Viewport一样大,在该示例中,ViewBox坐标系的1
单位和Viewport坐标系的1
单位相同(在该示例中就是1px
)。
但鸟的右下角顶点坐标是有变化的:
鸟右下角顶点坐标在ViewBox系统中(蓝色标尺)是(200, 300)
,对应的Viewport坐标系统中(灰色标尺)是(300, 400)
。
你可以尝试着调整不同的viewBox
属性的值,查看不同的效果,比如viewBox="-100 -100 400 300"
:
在该示例中,ViewBox坐标系统中的1
个单位相当于Viewport坐标系统中的2
个单位(该示例是2px
),因此你会发现,ViewBox坐标系统在Viewport坐标系统中向右向下移了200
个单位。反之,换成viewBox="-100 -100 1600 1200"
时,ViewBox坐标系统在Viewport坐标系统中只向右向下移了50
个单位:
特别声明,如果ViewBox的宽高比和Viewport宽高比不等同时,ViewBox坐标系统和Viewport坐标系统会变得更为复杂,这里不做阐述,感觉兴趣的话,可以在Demo中调整相应参数,但看效果,加强理解。
有关于SVG中这几个核心概念的更多介绍,还可以阅读@Sara Soueidan的《Understanding SVG Coordinate Systems and Transformations: The viewport, viewBox, and preserveAspectRatio》一文。
小结
在SVG的世界中,这几个概念是最核心的,也是最易于让人感到困惑和混淆。如果你对这个概念理解清楚了,那么后续使用SVG都不会再有难度和困惑。