彻底理解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 的设定可以达到缩放和移动的效果

参考资料

相关推荐
德育处主任1 个月前
前端啊,拿Lottie炫个动画吧
前端·svg·canvas
eggcode1 个月前
C#生成SVG文件(文本、线段、圆、椭圆、多边形的示例)
c#·svg
巽星石2 个月前
在VSCode中使用Excalidraw
vscode·svg·excalidraw
起来改bug2 个月前
svg画进度条
css·进度条·svg
孙中毅3 个月前
SVG 路径动画的实现原理
css·svg·动效
白雾茫茫丶4 个月前
Nest.js 实战 (七):如何生成 SVG 图形验证码
svg·nest.js
xachary4 个月前
手把手使用 SVG + CSS 实现渐变进度环效果
前端·css3·svg
xachary4 个月前
CSS mask-image 实现边缘淡出过渡效果
前端·css·css3·svg
leopai4 个月前
最受欢迎SVG图标库揭秘!为 React 应用注入矢量动力!
前端·react.js·svg
大漠_w3cpluscom4 个月前
聊聊 Web 中的圆
前端·css·svg