彻底理解SVG图片缩放与位移的原理 1

SVG是一种图片格式。在实际编程工作中,我们会经常遇到需要对svg图片进行同比缩放或者拖拽的场景。而往往svg图片,并不会那么听话。只有理解了svg缩放的原理,我们才能很好的解决此类问题。本篇内容采用一个独特的视角 - 将svg图片视为一个画布(Canvas),来帮助我们更容易的理解相关原理。

一、了解svg中的 Viewport 和 ViewBox

在svg中,空间的概念可以分为 Viewport 和 ViewBox 两个部分。从结构上可以看做一个div的边框和内部。这里,我们把 Viewport 视为相框,把 ViewBox 视为相片可以更好的理解这两个概念。

Viewport

Viewport比较好理解,就是相框的大小,也就是我们平时视觉能够看得到的范围。不管相框里面装着的相片实际有多大,我们最多能看到相片的范围,就相框那么大。在html中,我们可以通过设定 viewport 来调整相框的大小。

ViewBox

ViewBox可以想象成相片的大小。如果相片本身的尺寸和相框(viewport)一样大的时候,看起来不会有什么问题。我们可以从相框中看到一样大的相片。可是,如果相片(viewbox)的尺寸比相框大或者小时,就会麻烦一些。我们必须思考如何在相框中摆放相片,才能够呈现出自己想要的效果。ViewBox 就能够控制相片的大小,还能够控制相片如何在相框中摆放-也就是在相框中的位置。

1 viewport 等于 viewbox 时

通常,这处于svg的初始化阶段。首先,我们可以直接在svg元素上定义 viewport 的宽和高,当然可以用css定义。基本上,这个dom元素就是 viewport 的宽和高,也就是这个相框的宽高。接下来我们可以在svg中定义具体的图形内容,这里以一支鸟为实例:

html 复制代码
<svg id="svgElement" width="800" height="400">  
  <g><!-- 鸟的图形 --></g>  
</svg>

此时会得到如下的一个 viewport:

下面,我们可以设定 viewBox , viewBox 的设定可以写在 SVG 的标签上,设定的属性包含 <min-x> <min-y> <width> <height> 这四个,也就是 viewBox = '<min-x> <min-y> <width> <height>'

我们刚刚有提到,ViewBox 中除了能够控制的相片的大小之外,还能够控制相片要如何摆放在相框中,其中 <min-x><min-y> 就是在控制相片要如何摆放在相框中,而 <width><height> 则是相片的大小。

默认的情況下,viewBox 的尺寸大小会和 viewport 一樣,因此当我们把一只鸟的相片放入 svg 后,它会自动填满整个相框,两个的大小会是一样的,html代码会像这样:

html 复制代码
<svg id="svgElement" width="800" height="400" viewBox="0 0 800 400">  
  <g><!-- 要定义的图形内容 --></g>  
</svg>

相片会完整的融入在相框中:

viewBox的初始化尺寸会和viewport一样大小。

2 viewbox 小于 viewport 时: 图像会被放大

通过 viewBox 的设定,我们可以进一步放大或者缩小相片,这里有一个原则是:viewBox会自动的尽可能的去填满viewport的大小,说起来有点抽象,假设一开始我们的 viewport 宽高为: 800x400,现在我们可以通过 viewBox 的设定,使得 viewBox的大小变为 400x200,html代码如下:

html 复制代码
<svg id="svgElement" width="800" height="400" viewBox="0 0 400 200">  
  <g><!-- 鳥的圖形 --></g>  
</svg>

当相片的大小设定比相框小时,它会在原本的相片上裁剪一小块区域(这里是 400x200),接着把它调整到填满整个viewport的大小(800x400),分解过程如下:

  • 第一步:设定比 viewport 小的 viewBox
  • 第二步:剪裁
  • 第三步:填满

此时,会产生一个神奇的现象,虽然我们的相片尺寸(viewBox)比原本的要小,但是因为它会自动的去填满整个 viewport 的缘故,所以实际上相片被放大了。

3 viewbox 大于 viewport 时: 图像会被缩小

同样的原理,当我们设定的 viewBox 大于 viewport时,它会先把这张相片放大到指定的尺寸(比如我们将viewBox设定为1600x800),然后再尽可能的塞入 viewport 中去(这里是 800x400),html代码如下:

html 复制代码
<svg id="svgElement" width="800" height="400" viewBox="0 0 1600 800">  
  <g><!-- 鳥的圖形 --></g>  
</svg>

viewBox 设定为1600x800,比原本的 viewport 大。照片的底图会被放大,但是图案大小不会变。

将1600x800的 viewBox,塞进 800x400 的viewport当中,就会造成图案变小的情况。

所以虽然 viewBox 的设定(1600x800) 大于原始viewBox的大小(800x400),但是实际上,照片在显示上却会被缩小。

二、了解 SVG ViewBox位置的设定

上面提过,viewBox的设定,包含了四个属性 <min-x> <min-y> <width> <height>。在上面的示例中,我们使用了其中后面的2个:<width> <height>来控制viewBox的大小。而前两个属性,则是来设定相片的位置。

x代表横向,y代表纵向。当 x>0时,向左移动,反之向右移动。当y>0时向上移动,反之向下移动。

假如我们设定 viewBox的 min-x 为150时,即: viewBox = "150 0 800 400" ,viewBox会向左移动150个单位:

第一步:先移动

第二步:后裁剪。左边超出viewport的部分,会被遮盖,而右边则会空白:

我们也可以同时设定 min-x 和 min-y ,例如 viewBox = "-400 -200 800 400", 会得到这样的效果:

第一步:先移动

第二步:后裁剪。最后效果是这样的:

三、viewBox设定造成的影响

在viewBox的4个属性<min-x> <min-y> <width> <height>中,我们可以简单的理解成,前两项<min-x> <min-y> 控制的是位移(translate),能够实现左右移动的效果;后两项 <width> <height> 控制的是缩放(scale)。

但是因为viewBox实际上影响的是svg当中的坐标系统,所以会和我们的直觉相反。例如,当我们设定 min-x 和 min-y 越大时,实际上看到的画面会往左上方移动。同样,当我们设定width和height越大时,画面实际上看起来会缩小。

四、当viewport和viewBox的尺寸并不是等比例时:preserveAspectRatio

上文提到,当viewBox和viewport的尺寸大小不一样时,viewBox会尽可能的去塞满整个viewport。上面的例子,我们都把viewBox的尺寸大小设定的和viewport是等比的情况,如果是不同比例的情况下,我们该使用怎样的方式来实现对齐和填满呢?

这时,我们就需要用到 preserveAspectRatio 属性了。在这个属性当中,可以设定对齐的方式(align),用什么样的方式填满(meet 或者 slice)

在两者是等比的情况下,preserveAspectRatio 的值对画面没有任何影响,如果对于 preserveAspectRatio 的属性想要有更多的了解,可以参考這篇文章([译]理解 SVG 座標系統與 Transformations @ Andyyou),或進一步参考文章中最后面所列的参考文章。

五、深入理解 SVG 坐标系

为了帮助我们更进一步的探讨 SVG 坐标系统和 viewport 坐标系统,我们要來认识三种不同的座标名称,分别是 offsetclientSVG Point

其中 offset 和 client 所取得的值都是属于 viewport 坐标系统中的座标值(也就是 1px 就是真实的 1px),两者的差別在于 offset 是相对于 container 左上角的点,也就是以 container 左上角为(0, 0);而 client 则是相对于 window 左上角(浏览器窗口左上角)的点,以 window 左上角为(0, 0),越右 X 值越大,越下 Y 值越大。

SVG 坐标系统就比较特別一点了,它有专属的坐标点,可以通过 viewport 坐标系的 client 坐标加以转换。

1、 viewBox 等同于 viewport 时

我们用 Sara Soueidan 在所提供的互动案例来做更多說明,一开始的時候,我先把 viewBox 设成和 viewport 一样大,在这种情况下 SVG 昨天系统中的 1 单位大小会和 viewport 的一样大,也就是 1px。留意蓝色的尺标是 SVG 坐标系統、灰色的尺标则是 viewport 坐标系统:

同时我们标下鸟右下角的这个点,让我们后面能够更清楚 SVG 坐标系统的变化。在一开始的时候,因为 viewport 等同于 viewBox 的缘故,所以 SVG 坐标系统中这个点,和 viewport 坐标系统中的 offset 坐标点,都会是(200, 300):

2、 viewBox是viewport尺寸的一半时

接着,未能可以看到,当我把 viewBox 设为 viewport 的一半(viewBox = "0 0 400 300"),也就是让鸟看起來变 2 倍大时,这时候 SVG 坐标系统中的 1 单位,会变成 viewport 的 2 单位(这里就是 2px)。

但要留意的是,虽然图案被放大了两倍,但是鸟右下角这个在蓝色尺标的 SVG 坐标系统中一样是(200, 300),可是灰色尺标的 viewport 坐标系统中的 offset 坐标点会变成两倍,也就是(400, 600)。

3、当viewBox是viewport尺寸的两倍大时

同样的道理,当我把 viewBox 设为 viewport 的 2 倍大(viewBox = "0 0 1600 800"),也就是让鸟看起來变 2 倍大时,这时候 SVG 坐标系统中的 1 单位,会变成 viewport 的 1/2 单位(这里就是 0.5px)。

这时候你会看到,虽然蓝色尺标的 SVG 坐标系统中,右下角坐标仍然是(200, 300),但是 viewport 坐标系统中的 offset 坐标点则变成了(100, 150)。

总结

  • 知道有 viewport 和 SVG 这两种不同的坐标系統
  • 知道有 offset, client, SVGPoint 这三种不同的坐标点
  • 知道有 offset 和 client 属于 viewport 坐标系统;SVGPoint 属于 SVG 坐标系统
  • 知道通过 viewBox 的设定可以达到缩放和移动的效果

参考资料

相关推荐
用户98402276679182 天前
【React.js】渐变环形进度条
前端·react.js·svg
明远湖之鱼8 天前
opentype.js 使用与文字渲染
前端·svg·字体
wsWmsw9 天前
[译] 浏览器里的 Liquid Glass:利用 CSS 和 SVG 实现折射
前端·css·svg
CodeCraft Studio10 天前
CAD文件处理控件Aspose.CAD教程:在 Python 中将 SVG 转换为 PDF
开发语言·python·pdf·svg·cad·aspose·aspose.cad
红烧code21 天前
【Rust GUI开发入门】编写一个本地音乐播放器(4. 绘制按钮组件)
rust·gui·svg·slint
吃饺子不吃馅22 天前
AntV X6图编辑器如何实现切换主题
前端·svg·图形学
吃饺子不吃馅24 天前
深感一事无成,还是踏踏实实做点东西吧
前端·svg·图形学
吃饺子不吃馅25 天前
AntV X6 核心插件帮你飞速创建画布
前端·css·svg
吃饺子不吃馅1 个月前
揭秘 X6 核心概念:Graph、Node、Edge 与 View
前端·javascript·svg
吃饺子不吃馅1 个月前
如何让AntV X6 的连线“动”起来:实现流动效果?
前端·css·svg