您的位置:首页 > 编程语言 > ASP

SVG viewport、viewBox、preserveAspectRatio

2016-12-09 14:21 645 查看
转自 http://www.w3cplus.com/html5/svg-coordinate-systems.html

SVG元素不像HTML元素一样由CSS盒模型管理。这使得我们可以更加灵活定位和变换这些元素-也许一眼看上去不太直观。然而,一旦你理解了SVG坐标系和变换,操纵SVG会非常简单并且很有意义。本篇文章中我们将讨论控制SVG坐标系的最重要的三个属性:
viewport
, 
viewBox

和 
preserveAspectRatio


这是本系列三篇文章中的第一篇,这篇文章讨论SVG中的坐标系和变换。

理解SVG坐标系和变换(第一部分)-viewport,
viewBox
,和
preserveAspectRatio


理解SVG坐标系和变换(第二部分)-
transform
属性


理解SVG坐标系和变换(第三部分)-建立新视窗

为了使文中的内容和解释更形象化,我创建了一个互动演示,你可以任意改变
viewBox
 和 
preserveAspectRatio
的值。

在线案例

这个例子只是主要内容的一小部分,所以看完请回来继续阅读这篇文章


SVG画布

canvas是绘制SVG内容的一块空间或区域。理论上,画布在所有维度上都是无限的。所以SVG可以是任意尺寸。然而,SVG通过有限区域展现在屏幕上,这个区域叫做
viewport
。SVG中超出视窗边界的区域会被裁切并且隐藏。


视窗

视窗是一块SVG可见的区域。你可以把视窗当做一个窗户,透过这个窗户可以看到特定的景象,景象也许完整,也许只有一部分。

SVG的视窗类似访问当前页面的浏览器视窗。网页可以是任何尺寸;它可以大于视窗宽度,并且在大多数情况下都比视窗高度要高。然而,每个时刻只有一部分网页内容是透过视窗可见的。

整个SVG画布可见还是部分可见取决于这个
canvas
的尺寸以及
preserveAspectRatio
属性值。你现在不需要担心这些;我们之后会讨论更多的细节。

你可以在最外层
<svg>
元素上使用
width
height
属性声明视窗尺寸。
<!-- the viewport will be 800px by 600px -->
<svg width="800" height="600">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


在SVG中,值可以带单位也不可以不带。一个不带单位的值可以在用户空间中通过用户单位声明。如果值通过用户单位声明,那么这个值的数值被认为和
px
单位的数值一样。这意味着上述例子将被渲染为
800px*600px
的视窗。

你也可以使用单位来声明值。SVG支持的长度单位有:
em
ex
px
pt
pc
cm
mm
in
和百分比。

一旦你设定最外层SVG元素的宽高,浏览器会建立初始视窗坐标系和初始用户坐标系。


初始坐标系

初始视窗坐标系是一个建立在视窗上的坐标系。原点
(0,0)
在视窗的左上角,
X
轴正向指向右,
Y
轴正向指向下,初始坐标系中的一个单位等于视窗中的一个"像素"。这个坐标系统类似于通过CSS盒模型在HTML元素上建立的坐标系。

初始用户坐标系是建立在SVG画布上的坐标系。这个坐标系一开始和视窗坐标系完全一样-它自己的原点位于视窗左上角,
x
轴正向指向右,
y
轴正向指向下。使用
viewBox
属性,初始用户坐标系统-也称当前坐标系,或使用中的用户空间-可以变成与视窗坐标系不一样的坐标系。我们在一下节中讨论如何改变坐标系。

到现在为止,我们还没有声明
viewBox
属性值。SVG画布的用户坐标系统和视窗坐标系统完全一样。

下图中,视窗坐标系的"标尺"是灰色的,用户坐标系(
viewBox
)的是蓝色的。由于它们在这个时候完全相同,所以两个坐标系统重合了。



上面SVG中的鹦鹉的外框边界是
200
个单位(这个例子中是
200
个像素)宽和
300
个单位高。鹦鹉基于初始坐标系在画布中绘制。

新用户空间(即,新当前坐标系)也可以通过在容器元素或图形元素上使用
transform
属性来声明变换。我们将在这篇文章的第二部分讨论关于变换的内容,更多细节在第三部分和最后部分中讨论。


viewBox

我喜欢把
viewBox
理解为“真实”坐标系。首先,它是用来把SVG图形绘制到画布上的坐标系。这个坐标系可以大于视窗也可以小于视窗,在视窗中可以整体可见或部分可见。

在之前的章节里,这个坐标系-用户坐标系-和视窗坐标系完全一样。因为我们没有把它声明成其他坐标系。这就是为什么所有的定位和绘制看起来是基于视窗坐标系的。因为我们一旦创建视窗坐标系(使用
width
height
),浏览器默认创建一个完全相同的用户坐标系。

你可以使用
viewBox
属性声明自己的用户坐标系。如果你选择的用户坐标系统和视窗坐标系统宽高比(高比宽)相同,它会延伸来适应整个视窗区域(一分钟内我们就来讲个例子)。然而,如果你的用户坐标系宽高比不同,你可以用
preserveAspectRatio
属性来声明整个系统在视窗内是否可见,你也可以用它来声明在视窗中如何定位。我们会在下个章节里讨论这一情况的细节和例子。在这一章里,我们只讨论
viewBox
的宽高比符合视窗的情况-在这些例子中,
preserveAspectRatio
不产生影响。

在我们讨论这些例子前,我们回顾一下
viewBox
的语法。


viewBox
语法

viewBox
属性接收四个参数值,包括:
<min-x>,
<min-y>, width
 和 
height

viewBox = <min-x> <min-y> <width> <height>


<min-x>
 和 
<min-y>
 值决定viewBox的左上角,
width
height
决定视窗的宽高。这里要注意视窗的宽高不一定和父
<svg>
元素的宽高一样。
<width>
<height>
值为负数是不合法的。值为
0
的话会禁止元素的渲染。

注意视窗的宽度也可以在CSS中设置为任何值。例如:设置
width:100%
会让SVG视窗在文档中自适应。无论
viewBox
的值是多少,它会映射为外层SVG元素计算出的宽度值。

设置
viewBox
的例子如下:
<!-- The viewBox in this example is equal to the viewport, but it can be different -->
<svg width="800" height="600" viewBox="0 0 800 600">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


如果你之前在其他地方看到过
viewBox
,你也许会看到一些解释说你可以用
viewBox
属性通过缩放或者变化使SVG图形变换。这是真的。我将深入探究并且告诉你甚至可以使用
viewBox
来切割SVG图形。

理解
viewBox
和视窗之间差异最好的方法是亲身观察。所以让我们看一些例子。我们将从viewBox和viewport的宽高比相同的例子开始,所以我们还不需要深入了解
preserveAspectRatio


与viewport宽高比相同的
viewBox

我们从一个简单的例子开始。这个例子中的
viewBox
的尺寸是视窗尺寸的一半。在这个例子中我们不改变viewBox的原点,所以
<min-x>
<min-y>
都设置成
0
。viewBox的宽高是viewport宽高的一半。这意味着我们保持宽高比。
<svg width="800" height="600" viewBox="0 0 400 300">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


所以,
viewBox="0 0 400 300"
到底有什么用呢?

它声明了一个特定的区域,canvas横跨左上角的点
(0,0)
到点
(400,300)


SVG图像被这个区域裁切。

区域被拉伸(类似缩放效果)来充满整个视窗。

用户坐标系被映射到视窗坐标系-在这种情况下-一个用户单位等于两个视窗单位。

下面的图片展示了在我们例子中把上面的viewBox应用到
<svg>
 画布中的效果。灰色单位代表视窗坐标系,蓝色坐标系代表
viewBox
建立的用户坐标系。



任何在SVG画布中画的内容都会被对应到新的用户坐标系中。

我喜欢像Google地图一样通过
viewBox
把SVG画布形象化。在Google地图中你可以在特定区域缩放;这个区域是唯一可见的,并且在浏览器视窗中按比例增加。然而,你知道地图的剩余部分还在那里,但是不可见因为它超出视窗的边界-被裁切了。

现在让我们试着改变
<min-x>
<min-y>
的值。都设置为
100
。你可以设置成任何你想要的值。宽高比还是和视窗的宽高比一样。
<svg width="800" height="600" viewBox="100 100 200 150">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


添加
viewBox="100 100 200 150"
的效果和之前例子中一样都是裁切的效果。图形被裁切然后拉伸来充满整个视窗区域。



再一次,用户坐标系被映射到视窗坐标系-200用户单位映射为800视窗单位因此每个用户单位等于四个视窗单位。结果像你看到的那样是放大的效果。

另外注意,在这个时候,为
<min-x>
<min-y>
声明非0的值对图形有变换的效果;更加特别的是,SVG
画布看起来向上拉伸100个单位,向左拉伸100个单位(
transform="translate(-100
-100)"
)。

的确,作为规范说明,“
viewBox
属性的影响在于用户代理自动添加适当的变换矩阵来把用户空间中具体的矩形映射到指定区域的边界(通常是视窗)”。

这是一个很棒的说明我们之前已经提到的内容的方法:图形被裁切然后被缩放以适应视窗。这个说明随后增加了一个注释:“在一些情况下用户代理在缩放变换之外需要增加一个移动变换。例如,在最外层的svg元素上,如果viewBox属性对
<min-x>
<min-y>
声明非0值得那么就需要移动变换。”

为了更好演示移动变换,让我们试着给
<min-x>
<min-y>
添加-100。移动效果类似
transform="translate(100
100)"
;这意味着图形会在切割和缩放后移动到右下方。回顾倒数第二个裁切尺寸为400*300的例子,添加新的无效
<min-x>
<min-y>
值,新的代码如下:
<svg width="800" height="600" viewBox="-100 -100 300 200">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


给图形添加上述
viewBox
 transformation的结果如下图所示:



注意,与
transform
属性不同,因为
viewBox
自动添加的
tranfomation
不会影响有
vewBox
属性的元素的
x
,
y
,宽和高等属性。因此,在上述例子中展示的带有
width
,
height
viewBox
属性的
svg
元素,
width
height
属性代表添加
viewBox
 变换之前的坐标系中的值。在上述例子中你可以看到初始(灰色)viewport坐标系甚至在
<svg>
上使用了
viewBox
属性后仍然没有影响。

另一方面,像
tranform
属性一样,它给所有其他属性和后代元素建立了一个新的坐标系。你还可以看到在上述例子中,用户坐标系是新建立的-它不是保持像初始用户坐标系和使用
viewBox
前的视窗坐标系一样。任何
<svg>
后代会在这个新的用户坐标系中定位和确定尺寸,而不是初始坐标系。

最后一个
viewBox
的例子和前一个类似,但是它不是切割画布,我们将在viewport里扩展它并看它如何影响图形。我们将声明一个宽高比视窗大的viewBox,并依然保持viewport的宽高比。我们在下一章里讨论不同的宽高比。

在这个例子中,我们将
viewBox
的尺寸设为viewport的
1.5
倍。
<svg width="800" height="600" viewBox="0 0 1200 900">
<!-- SVG content drawn onto the SVG canvas -->
</svg>


现在用户坐标系会被放大到1200*900。它会被映射到视窗坐标系,用户坐标系中的每一个单位水平方向上等于视窗坐标系中的
viewport-width
/ viewBox-width
,竖直方向上等于
viewport-height / viewBox-height
。这意味着,在这种情况下,每一个用户坐标系中的
x-units
等于viewport坐标系中的
0.66
x-units
,每个用户
y-unit
映射成
0.66
的viewport
y-units。

当然,理解这些最好的方法是把结果视觉化。
viewBox
被缩放到适应下图所示的viewport。因为图形在画布里基于新的用户坐标系绘制的,而不是视窗坐标系,它看起来比视窗小。



到目前为止,我们所有的例子的宽高比都和视窗一致。但是如果
viewBox
中声明的宽高比和视窗中的不一样会发生什么呢?例如,试想我们把视窗的尺寸设为1000*500。宽高比不再和视窗的一样。在例子中使用
viewBox="0
0 1000 500"
的结果如下图:



用户坐标系。因此图形在视窗中定位:

整个
viewBox
适应视窗。

保持
viewBox
的宽高比。
viewBox
没有被拉伸来覆盖视窗区域。

viewBox
在视窗中水平垂直居中。

这是默认表现。那用什么控制表现呢?如果我们想改变视窗中
viewBox
的位置呢?这就需要用到
preserveAspectRatio
属性了。


preserveAspectRatio
属性

preserveAspectRatio
属性强制统一缩放比来保持图形的宽高比。

如果你用不同于视窗的宽高比定义用户坐标系,如果像我们在之前的例子中看到的那样浏览器拉伸viewBox来适应视窗,宽高比的不同会导致图形在某些方向上扭曲。所以如果上一个例子中的
viewBox
被拉伸以在所有方向上适应视窗,图形看起来如下:



当给
viewBox
设置
0
0 200 300
的值时扭曲显而易见(显然这很不理想),这个值小于视窗尺寸。我故意选择这个尺寸从而让
viewBox
匹配鹦鹉边界盒子的尺寸。如果浏览器拉伸图像来适应整个视窗,看起来会像下面这样:



preserveAspectRatio
属性让你可以在保持宽高比的情况下强制统一
viewBox
的缩放比,并且如果不想用默认居中你可以声明
viewBox
在视窗中的位置。


preserveAspectRatio
语法

preserveAspectRatio
的官方语法是:
preserveAspectRatio = defer? <align> <meetOrSlice>?


它在任何建立新viewport的元素上都有效(我们会在这个系列的下一部分讨论这个问题)。

defer
声明是可选的,并且只有当你在
<image>
上添加
preserveAspectRatio
才被用到。用在任何其他元素上时它都会被忽略。
<images>
本身不在这篇文章的讨论范围,我们暂时跳过
defer
这个选项。

align
参数声明是否强制统一放缩,如果是,对齐方法会在
viewBox
的宽高比不符合viewport的宽高比的情况下生效。

如果
align
值设为
none
,例如:
preserveAspectRatio = "none"


图形不在保持宽高比而会缩放来适应视窗,像我们在上面两个例子中看到的那样。

其他所有
preserveAspectRatio
值都在保持viewBox的宽高比的情况下强制拉伸,并且指定在视窗内如何对齐viewBox。我们会简短介绍
align
的值。

最后一个属性,
meetOrSlice
也是可选的,默认值为
meet
。这个属性声明整个
viewBox
在视窗中是否可见。如果是,它和
align
参数通过一个或多个空格分隔。例如:
preserveAspectRatio = "xMinYMin slice"


这些值第一眼看起来也许很陌生。为了让它们更易于理解和熟悉,你可以把
meetOrSlice
的值类比于
background-size
contain
cover
值;它们非常类似。
meet
类似于
contain
slice
类似于
cover
。下面是每个值的定义和含义:


meet
(默认值)

基于以下两条准侧尽可能缩放元素:

保持宽高比

整个
viewBox
在视窗中可见

在这个情况下,如果图形的宽高比不符合视窗,一些视窗会超出
viewBox
的边界(即
viewBox
绘制的区域会小于视窗)。(在
viewBox
一节查看最后的例子。)在这个情况下,
viewBox
的边界被包含在viewport中使得边界满足。

这个值类似于
background-size: contain
。背景图片在保持宽高比的情况下尽可能缩放并确保它适合背景绘制区域。如果背景的长宽比和应用的元素的长宽比不一样,部分背景绘制区域会没有背景图片覆盖。


slice

在保持宽高比的情况下,缩放图形直到
viewBox
覆盖了整个视窗区域。
viewBox
被缩放到正好覆盖视窗区域(在两个维度上),但是它不会缩放任何超出这个范围的部分。换而言之,它缩放到
viewBox
的宽高可以正好完全覆盖视窗。

在这种情况下,如果
viewBox
的宽高比不适合视窗,一部分
viewBox
会扩展超过视窗边界(即,
viewBox
绘制的区域会比视窗大)。这会导致部分
viewBox
被切片。

你可以把这个类比为
background-size: cover
。在背景图片的情况中,图片在保持本身宽高比(如何)的情况下缩放到宽高可以完全覆盖背景定位区域的最小尺寸。

所以,
meetOrSlice
被用来声明
viewBox
是否会被完全包含在视窗中,或者它是否应该尽可能缩放来覆盖整个视窗,甚至意味着部分的
viewBox
会被“slice”。

例如,如果我们声明
viewBox
的尺寸为
200*300
,并且使用了
meet
slice
值,保持
align
值为浏览器默认,每个值的结果会看起来如下:



align
参数使用9个值中的一个或者为
none
。任何除
none
之外的值都用来保持宽高比缩放图片,并且还用来在视窗中对齐
viewBox


当使用百分比值时,
align
值类似于
background-position
。你可以把viewBox当做背景图像。通过
align
定位和
background-position
的不同在于,不同于通过一个与视窗相关的点来声明一个特定的viewBox值,它把具体的viewBox“轴”和对应的视窗的“轴”对齐。

为了理解每个
align
值的含义,我们将首先介绍每一个“轴”。

还记得
viewBox
<min-x>
<min-y>
值吗?我们将使用它们来定义
viewBox
中的"min-x"和"min-y"轴。另外,我们将定义两个轴“max-x”和”max-y“,各自通过
<min-x>
+ <width>
 和 
<min-y> + <height>
来定位。最后,我们定义两个轴"mid-x"和"mid-y",根据
<min-x>
+ (<width>/2)
 和 
<min-y> + (<height>/2)
来定位。

这样做是不是让事情更复杂了呢?如果是这样,让我们看一下下面的图片来看一下每个轴代表了什么。在这张图片中,
<min-x>
和 
<min-y>
值都设置为0。
viewBox
被设置为
viewBox
= "0 0 300 300"




上面图片中的灰色虚线代表视窗的
mid-x
mid-y
轴。我们将对它们赋一些值来对齐
viewBox
mid-x
mid-y
轴。对于视窗,
min-x
的值等于
0
min-y
值也等于
0
max-x
值等于
viewBox
的宽度,
max-y
的值等于高度,
mid-x
mid-y
代表了宽度和高度的中间值。

对齐的取值包括:


none

不强制统一缩放。如果必要的话,在不统一(即不保持宽高比)的情况下缩放给定元素的图像内容直到元素的边界盒完全匹配是视窗矩形。

换句话说,如果有必要的话
viewBox
被拉伸或缩放来完全适应整个视窗,不管宽高比。图形也许会扭曲。

(注意:如果
<align>
的值是none,可选的
<meetOrSlice>
值无效。)


xMinYMin

强制统一缩放

视窗X轴的最小值对齐元素
viewBox
<min-x>


视窗Y轴的最小值对齐元素viewBox的
<min-y>


把这个类比为
backrgound-position: 0% 0%;


xMinYMid

强制统一缩放。

视窗X轴的最小值对齐元素
viewBox
<min-x>


视窗Y轴的中间值来对齐元素的viewBox的中间值。

把这个类比为
backrgound-position: 0% 50%;


xMinYMax

强制统一缩放。

视窗X轴的最小值对齐元素
viewBox
<min-x>


视窗X轴的最大值对齐元素的
viewBox
<min-y>+<height>


把这个类比为
backrgound-position: 0% 100%;


xMidYMin

强制统一缩放。

视窗X轴的中间值对齐元素的
viewBox
的X轴中间值。

视窗Y轴的中间值对齐元素的
viewBox
的 
<min-y>


把这个类比为
backrgound-position: 50% 0%;


xMidYMid
 (默认值)

强制统一缩放。

视窗X轴的中间值对齐元素的
viewBox
的X轴中间值。

视窗Y轴的中间值对齐元素的
viewBox
的Y轴中间值。

把这个类比为
backrgound-position: 50% 50%;


xMidYMax

强制统一缩放。

视窗X轴的中间值对齐元素的
viewBox
的X轴中间值。

视窗Y轴的最大值对齐元素的
viewBox
<min-y>+<height>


把这个类比为
backrgound-position: 50% 100%;


xMaxYMin

强制统一缩放。

视窗X轴的最大值对齐元素的
viewBox
的 
<min-x>+<width>


视窗Y轴的最小值对齐元素的
viewBox
<min-y>


把这个类比为
backrgound-position: 100% 0%;


xMaxYMid

强制统一缩放。

视窗X轴的最大值对齐元素的
viewBox
的 
<min-x>+<width>


视窗Y轴的中间值对齐元素的
viewBox
的Y轴中间值。

把这个类比为
backrgound-position: 100% 50%;


xMaxYMax

强制统一缩放。

视窗X轴的最大值对齐元素的
viewBox
的 
<min-x>+<width>


视窗Y轴的最大值对齐元素的
viewBox
的 
<min-y>+<height>


把这个类比为
backrgound-position: 100% 100%;


所以,通过使用
preserveAspectRatio
属性的
align
meetOrSlice
值,你可以声明是否统一缩放
viewBox
,是否和视窗对齐,在视窗中是否整个可见。

有时候,取决于
viewBox
的尺寸,一些值可能会导致相似的结果,例如在早先
viewBox="0
0 200 300"
的例子中,一些对齐完全用了不同的
align
值。这时候就要设置
meetOrSlice
的值为
meet
来保证
viewBox
包含在viewport内。



如果我们把
meetOrSlice
的值改成
slice
,不同的值我们将得到不同的结果。注意
viewBox
是如何拉伸来覆盖整个视窗的。
x
轴被拉伸到用
200
单位来覆盖视窗
800
单位。为了达到这个目的,并且保持
viewBox
的宽高比,
y
轴在底部被“裁切”,但是你可以想象它在视窗中高度上的延伸。



当然,不同的
viewBox
值看起来不同于我们这里用的
200*300
。为了保持简洁,我们不再列举更多的例子,你可以看我创建的一些互动演示来帮助你更好地形象化理解
viewBox
preserveAspectRatio
在不同值下的效果。你可以在一下节中查看互动演示例子的链接。

但是在这之前,我想要提醒你注意如果
<min-x>
 和 
<min-y>
值改变,那么
mid-x
mid-y
max-x
,
和 
max-y
的值也会发生改变。你可以在互动演示中改变这些值来查看轴以及相关联的
viewBox
的对齐方式的改变。

下面图片展示了定位轴的位置为
viewBox = "100 0 200 300"
时的效果。和之前用一样的例子,但是我们把
<min-x>
的值设为
100
而不是之前的
0
。你可以设置成任何你想要的值。注意
min-x
mid-x
,
和 
max-x
轴是如何变化的。这里使用的
preserveAspectRatio
值为默认的
xMinYMin
meet
,意味着
mid-*
轴和视窗轴的中间对齐。




互动演示

要理解viewport, 
viewBox
, 以及不同的
preserveAspectRatio
值是如何工作的最好方法是可视化的演示。

出于这个目的,我创建了一个简单的互动演示,你可以改变这些属性的值来查看新值导致的结果。



在线案例

我希望这篇文章在帮助你理解SVG viewport, 
viewBox
, 和 
preserveAspectRatio
 内容时有作用。如果你想要了解更多关于SVG坐标系的内容,例如嵌套坐标系,建立一个新的坐标系以及SVG中的变换,继续阅读这一系列接下来的部分。感谢你的阅读!

本文根据SaraSoueidan的《Understanding
SVG Coordinate Systems and Transformations (Part 1) — The viewport, viewBox, and preserveAspectRatio》一文所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处http://sarasoueidan.com/blog/svg-coordinate-systems/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: