理解SVG坐标系统和变换: 建立新视窗

理解SVG坐标系统和变换: transform属性

2015/09/23 · HTML5 ·
SVG

原文出处:
SaraSoueidan   译文出处:Blueed@Ivan_z3   

SVG元素可以通过缩放,移动,倾斜和旋转来变换-类似HTML元素使用CSS
transform来变换。然而,当涉及到坐标系时这些变换所产生的影响必然有一定差别。在这篇文章中我们讨论SVG的transform属性和CSS属性,包括如何使用,以及你必须知道的关于SVG坐标系变换的知识。

这是我写的SVG坐标系统和变换部分的第二篇。在第一篇中,包括了任何要理解SVG坐标系统基础的需要知道的内容;更具体的是,
SVG viewport, viewBox 和 preserveAspectRatio 属性。

  • 理解SVG坐标系和变换(第一部分)-viewport,viewBox,和preserveAspectRatio
  • 理解SVG坐标系和变换(第二部分)-transform属性
  • 理解SVG坐标系和变换(第三部分)-建立新视窗

这一部分我建议你先阅读第一篇,如果没有,确保你在阅读这篇之前已经读了第一篇。

前面的话

  前面介绍过SVG视野后,本文将开始介绍SVG坐标系统及图形变换

 

理解SVG坐标系统和变换: 建立新视窗

2015/09/23 · HTML5 ·
SVG

原文出处:
SaraSoueidan   译文出处:Blueed@Ivan_z3   

在SVG绘制的任何一个时刻,你可以通过嵌套<svg>或者使用例如<symbol>的元素来建立新的viewport和用户坐标系。在这篇文章中,我们将看一下我们如何这样做,以及这样做如何帮助我们控制SVG元素并让它们变得更加灵活(或流动)。

这是SVG坐标系和变换系列的第三篇也是最后一篇文章。在第一篇中,包括了任何要理解SVG坐标系统基础的需要知道的内容;更具体的是,
SVG
viewport, viewBox和 preserveAspectRatio属性。在第二篇文章里,你可以了解到任何你需要了解的关于SVG系统变换的内容。

  • 理解SVG坐标系和变换(第一部分)-viewport,viewBox,和preserveAspectRatio
  • 理解SVG坐标系和变换(第二部分)-transform属性
  • 理解SVG坐标系和变换(第三部分)-建立新视窗

通过这篇文章,我假定你已经读了这个系列的第一部分关于SVG
viewport, viewBox 和 preserveAspectRatio 属性的内容
。在阅读这篇文章之前你不需要读第二篇关于坐标系变换的内容。

理解SVG transform坐标变换

2015/10/11 · HTML5 ·
SVG

原文出处:
张鑫旭   

transform属性值

tranform属性用来对一个元素声明一个或多个变换。它输入一个带有顺序的变换定义列表的<transform-list>值。每个变换定义由空格或逗号隔开。给元素添加变换看起来如下:

有效地SVG变换有:旋转缩放移动,
倾斜transform属性中使用的变换函数类似于CSS中transform属性使用的CSS变换函数,除了参数不同。

注意下列的函数语法定义只在transform属性中有效。查看section about
transforming SVGs with CSS
properties获取关于CSS变换属性中使用的语法信息。

坐标定位

  对于所有元素,SVG使用的坐标系统或者说网格系统,和Canvas用的差不多(所有计算机绘图都差不多)。这种坐标系统是:以页面的左上角为(0,0)坐标点,坐标以像素为单位,x轴正方向是向右,y轴正方向是向下

图片 1

  定义一个矩形,即从左上角开始,向右延展100px,向下延展100px,形成一个100*100大的矩形

<rect x="0" y="0" width="100" height="100" />

 

嵌套<svg>元素

在第一部分我们讨论了<svg>元素如何为SVG画布内容建立一个视窗。在SVG绘制过程中的任何一个时刻,你可以创建一个新的视窗其中包含的图形是通过把一个<svg>元素包含在另一个中绘制的。通过建立新视窗,你隐性得建立了一个新视窗坐标系和新用户坐标系。

例如,试想有一个<svg>以及里面的内容:

XHTML

<svg xmlns=””
xmlns:xlink=”; <!– some SVG content
–> <svg> <!– some inner SVG content –> </svg>
<svg>

1
2
3
4
5
6
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!– some SVG content –>
    <svg>
        <!– some inner SVG content –>
    </svg>
<svg>

 

第一件需要注意的是内容<svg>元素不需要声明一个命名空间xmlns因为默认和外层<svg>的命名空间相同。当然,如果在HTML5文档中外层<svg>也不需要命名空间。

你可以使用一个嵌套的SVG来把元素组合在一起然后在父SVG中定位它们。现在,你也可以把元素组合在一起并且使用组<g>来定位-通过把元素包括在一组<g>元素中。你可以使用transform属性在画布中定位它们。然而,使用<svg>肯定好过使用<g>。使用x和y坐标来定位,在许多情况下,比使用变换更加方便。另外,<svg>元素接受宽高值,<g>不行。这意味着,<svg>也许并必要的,因为它可以创建一个新的viewport和坐标系,你可以不需要也不想要。

通过给<svg>声明宽高值,你把内容限制在通过x,y,widthheight属性定义的viewport的边界。任何超过边界的内容会被裁切。

如果你不声明xy属性,它们默认是0。如果你不声明heightwidth属性,<svg>会是父SVG宽度和高度的100%。

另外,声明用户坐标系而不是默认的也会影响内部<svg>的内容。

<svg>内的元素百分比值的声明会根据<svg>计算,而不是外层<svg>。例如,下面的代码会导致内层SVG等于400单位,里面的长方形是200个单位:

XHTML

<svg width=”800″ height=”600″> <svg width=”50%” ..> <rect
width=”50%” … /> </svg> </svg>

1
2
3
4
5
<svg width="800" height="600">
    <svg width="50%" ..>
        <rect width="50%" … />
    </svg>
</svg>

 

如果最外层<svg>的宽度为100%(例如,如果它在一个文档中内联或者你想要它可以流动),内层SVG会扩展拉伸来保持宽度为外层SVG的一半-这是强制的。

嵌套SVG在给SVG画布中的元素增加灵活性和扩展性时尤其有用。我们知道,使用viewBox值和preserveAspectRatio,我们已经可以创建响应式SVG。最外层<svg>的宽度可以设置成100%来确保它扩展拉伸到它的容器(或页面)扩展或拉伸。然后通过使用viewBox值和
preserveAspectRatio,我们可以保证SVG画布可以自适应viewport中的改变(最外层svg)。我在CSSConf演讲的幻灯片中写到了关于响应式SVG的内容。你可以在这里查看这个技术。

然而,当我们像这样创建一个响应式SVG,整个画布以及所有绘制在上面的元素都会有反应并且同时改变。但有时候,你只想让图形中的一个元素变为响应式,并且保持其他东西“固定”在一个位置和/或尺寸。这时候嵌套svg就很有用。

svg元素有独立于它父元素的坐标系,它可以有独立的viewBoxpreserveAspectRatio属性,你可以任意修改里面内容的尺寸和位置。

所以,要让一个元素更加灵活,我们可以把它包裹在<svg>元素中,并且给svg一个弹性的宽度来适应最外层SVG的宽度,然后声明preserveAspectRatio="none"这样的话里面的图形会扩展和拉伸到容器的宽度。注意svg可以多层嵌套,但是为了让事情简洁,我在这篇文章里只嵌套一层深度。

为了演示嵌套svg如何发挥作用,让我们来看一些例子。

一、HTML transform和SVG transform

SVG中自带transform属性,没错,是属性,例如:

JavaScript

<svg width=”200″ height=”150″> <rect x=”30″ y=”30″ width=”120″
height=”90″ transform=”rotate(45)”></rect> </svg>

1
2
3
<svg width="200" height="150">
    <rect x="30" y="30" width="120" height="90" transform="rotate(45)"></rect>
</svg>

普通的HTML元素没有transform属性,但是支持CSS3的transform,
好奇的小伙伴可能会疑问了,CSS3中的transform变换,跟SVG中的transform是什么关系呢?

恩,有点类似于谢霆锋和陈冠希之间的关系,有些小复杂。

图片 2

OK, 先说说相似之处吧。
一些基本的变换类型是一样的,包括:位移translate, 旋转rotate,
缩放scale, 斜切skew以及直接矩阵matrix.
但只局限于2D层面的变换。SVG似乎只支持二维变换(若有不对,欢迎指正),且类似translateXrotateX也都是不支持的。

下面就是不一样的地方了:
1. CSS3 transform一般用在普通元素上,虽然也可以应用在SVG元素上,但是IE浏览器(IE
edge未测试)却不支持SVG元素;

JavaScript

rect { /* IE说:你这是弄啥来? */ transform:rotate(45deg); }

1
2
3
4
rect {
    /* IE说:你这是弄啥来? */
    transform:rotate(45deg);
}

2. HTML元素的CSS3 transform和SVG的transform坐标系统大相径庭;

平常我们使用transform其坐标是相对于当前元素而言的,默认是元素的中心点变换,我们可以通过transform-origin属性改变变换的中心点。而SVG中的transform的坐标变换的是相对于画布的左上角计算的,跟HTML的transform差别较大,理解上也更加麻烦。而本文就是彻底理清SVG中的transform到底是怎么工作的。

3. 具体的语法细节有差异。SVG transform属性语法有些自带偏移。而CSS transform则更加纯粹些。

//zxx: 据说CSS的transform和SVG的transform属性即将合并。

Matrix

你可以使用matrix()函数在SVG元素上添加一个或多个变换。matrix变换语法如下:

matrix(<a> <b> <c> <d> <e> <f>)

1
matrix(<a> <b> <c> <d> <e> <f>)

上述声明通过一个有6个值的变换矩阵声明一个变换。matrix(a,b,c,d,e,f)等同于添加变换matrix[a b c d e f]

如果你不精通数学,最好不要用这个函数。对于那些精通的人,你可以在这里阅读更多关于数学的内容。因此这个函数很少使用-我将忽略来讨论其他变换函数。

四个坐标系

  SVG中的四个坐标系包括用户坐标系、自身坐标系、前驱坐标系和参考坐标系。其中,用户坐标系和自身坐标系是客观的坐标系,而前驱坐标系和参考坐标系是相对的坐标系

【用户坐标系】

  SVG的世界是无穷大的,世界里面的坐标系就是用户坐标系

  viewBox的设置就是观察用户坐标系的哪个区域。比如设置viewBox = “0 0
200 150″,即观察用户坐标系里的这个区域

图片 3
  用户坐标系是最原始的坐标系,其他的坐标系都是基于用户坐标系产生的。因此,用户坐标系也被称为原始坐标系

【自身坐标系】

  自身坐标系是每个SVG图形或图形分组与生俱来的坐标系

  比如一个矩形设置为<rect x=”0″ y=”0″ width=”100″
height=”100″/>,其位置和尺寸的定义都是基于其自身坐标系来描述的

图片 4

【前驱坐标系】

  前驱坐标系即该元素的父元素的坐标系

  如下所示,矩形rect的父元素是SVG元素,即其前驱坐标系是用户坐标系

<svg version="1.1" width="300" height="70" xmlns="http://www.w3.org/2000/svg">
  <rect x="0" y="0" width="100" height="50"/>
</svg> 

坐标变换

  所谓坐标变换,指前驱坐标系经过元素的变换后,得到元素的自身坐标系

  如下所示,加入transform属性之后,rect元素就是相对于其前驱坐标系发生了坐标变换,得到了自身坐标系

图片 5

【参考坐标系】

  使用其他坐标系来观察自身元素坐标位置时使用

  如下所示,rect元素定义的坐标是(0,0)。如果以用户坐标系作为参考坐标系,则rect元素在参考坐标系中的坐标是(50,50)

图片 6

 

例子

试想我们有如下的SVG:图片 7

上述SVG是响应式的。改变屏幕的尺寸会导致整个SVG图形根据需要做出反应。下面的截图展示了拉伸页面的结果,以及SVG如何变得更小。注意SVG的内容如何根据SVG视窗和相互之间保持它们的初始位置。图片 8

使用嵌套SVG,我们将改变这个情况。我们可以对SVG中每个独立的元素根据SVG视窗声明一个位置,所以随着SVG
视窗尺寸的改变(即最外层svg的改变),每个元素独立于其他元素发生改变。

注意,在这个时候,你需要熟悉SVG viewport, viewBox,
preserveAspectRatio是如何生效的。

我们将要创建一个效果,当屏幕尺寸变化时,蛋壳的上部分移动使得其中的可爱的小鸡显示出来,如下图所示:图片 9

为了达到这个效果,蛋的上半部分必须和其他部分分离出来单独包含一个自己的svg。这个svg包含框会有一个IDupper-shell

然后,我们保证新的svg#upper-shell和外层SVG有一样的高度和宽度。可以通过在svg上声明width="100%"``height="100%"或者不声明任何高度和宽度来实现。如果内层SVG上没有声明任何宽高,它会自动扩展为外层SVG宽高的100%

最终,为了确保上壳被“抬”起或定位在svg#upper-shell顶部的中心,我们将使用适当的preserveAspectRatio值来确保viewBox被定位在视窗的顶部中心-值是xMidYMin

SVG图形的代码如下:

XHTML

<svg version=”1.1″ xmlns=””
xmlns:xlink=”; <!– … –>
<svg viewBox=”0 0 315 385″ preserveAspectRatio=”xMidYMid meet”>
<!– the chicken illustration –> <g id=”chicken”> <!–
… –> </g> <!– path forming the lower shell –>
<path id=”lower-shell” fill=”url(#gradient)” stroke=”#000000″
stroke-width=”1.5003″ d=”…”/> </svg> <svg id=”upper-shell”
viewBox=”0 0 315 385″ preserveAspectRatio=”xMidYMin meet”> <!–
path forming the upper shell –> <path id=”the-upper-shell”
fill=”url(#gradient)” stroke=”#000000″ stroke-width=”1.5003″
d=”…”/> </svg> </svg>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!– … –>
    <svg viewBox="0 0 315 385" preserveAspectRatio="xMidYMid meet">
        <!– the chicken illustration –>
        <g id="chicken">
            <!– … –>
        </g>
        <!– path forming the lower shell –>
        <path id="lower-shell" fill="url(#gradient)" stroke="#000000" stroke-width="1.5003" d="…"/>
    </svg>
 
    <svg id="upper-shell" viewBox="0 0 315 385" preserveAspectRatio="xMidYMin meet">
        <!– path forming the upper shell –>
        <path id="the-upper-shell" fill="url(#gradient)" stroke="#000000" stroke-width="1.5003" d="…"/>
    </svg>
</svg>

这个时候,注意在嵌套svg#upper-shell上声明的viewBox和最外层svg有相同的值(在它被移除之前)。我们用相同的viewBox值我原因就是这样,SVG在大屏幕上保持最初的样子。

所以,这件事是这样的:我们开始一个SVG-在我们的例子中,这是一张里面藏着一个小鸡的带裂纹的蛋。然后,我们创建了另一“层”并把上部分的壳放在里面-这一层通过使用嵌套svg创建。嵌套svg和外层svg的尺寸和viewBox一样。最终,内层SVG的viewBox被设置成不管屏幕尺寸是多少都“固定”在viewport的顶部-这确保了当屏幕尺寸很窄时SVG被拉长,上层的壳被向上举起,因此展示出“隐藏”在里面的小鸡。图片 10

一旦屏幕尺寸拉伸,SVG被拉长,使用preserveAspectratio="xMidYMin meet"把包含上部分壳的viewBox被定位到viewport的顶部。图片 11

点击下面按钮来查看在线SVG。记住改变屏幕尺寸再看SVG变化。

在线案例

嵌套或”分层”SVG使你可以根据改变的视窗定位SVG的一部分,在保持元素宽高比的情况下。所以图片可以在不扭曲内容元素的情况下自适应。

如果我们想要整个鸡蛋剥离显示出小鸡,我们可以单独用一个svg层包含下部分壳,viewBox也相同。确保下部分壳向下移动并固定在视窗的底部中心,我们使用preserveAspectRatio="xMidYMax meet"来定位。代码如下:

XHTML

<svg version=”1.1″ xmlns=””
xmlns:xlink=”; <svg id=”chick”
viewBox=”0 0 315 385″ preserveAspectRatio=”xMidYMid meet”> <!–
the chicken illustration –> <g id=”chick”> <!– … –>
</g> </svg> <svg id=”upper-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”> <!– path forming the upper
shell –> <path id=”the-upper-shell” fill=”url(#gradient)”
stroke=”#000000″ stroke-width=”1.5003″ d=”…”/> </svg>
<svg id=”lower-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMax meet”> <!– path forming the lower
shell –> <path id=”the-lower-shell” fill=”url(#gradient)”
stroke=”#000000″ stroke-width=”1.5003″ d=”…”/> </svg>
</svg>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <svg id="chick" viewBox="0 0 315 385" preserveAspectRatio="xMidYMid meet">
        <!– the chicken illustration –>
        <g id="chick">
            <!– … –>
        </g>
    </svg>
 
    <svg id="upper-shell" viewBox="0 0 315 385" preserveAspectRatio="xMidYMid meet">
        <!– path forming the upper shell –>
        <path id="the-upper-shell" fill="url(#gradient)" stroke="#000000" stroke-width="1.5003" d="…"/>
    </svg>
 
    <svg id="lower-shell" viewBox="0 0 315 385" preserveAspectRatio="xMidYMax meet">
        <!– path forming the lower shell –>
        <path id="the-lower-shell" fill="url(#gradient)" stroke="#000000" stroke-width="1.5003" d="…"/>
    </svg>
</svg>

每个svg层/viewport等于最外层svg宽高的100%。所以我们基本有了三个副本。每层包含一个元素-上部分壳,下部分壳,或小鸡。三层的viewBox是相同的,只有preserveAspectRatio不同。图片 12

当然,在这个例子里,一开始的图形中小鸡隐藏在蛋里,随着屏幕变小才显示出来。然而,你可以做一些不一样的:你可以开始在小屏幕上创建一个图形,然后在大屏幕上显示一些东西;即当svg变宽时才有更多垂直空间来展示元素。

你可以更有创造性,根据不同屏幕尺寸来显示和隐藏元素-使用媒体查询-把新元素通过特定方式定位来达到特定的效果。想象力是无穷的。

同时注意嵌套svg不需要和容器svg有相同的宽高;你可以声明宽高并且限制svg内容,超出边界裁切-这都取决于你想要达到什么效果。

二、SVG transform translate位移

我们先来看下最简单最基本的translate位移变换,例如,我们偏移(295,115)大小的位置,HTML元素的偏移(下图左)和SVG元素的偏移(下图右)就会不一样。一个是相对自己的中心点(下图左),一个是SVG的左上角(下图右)。

图片 13

虽然两者的相对位置不一样,但是,对于单纯地位移来讲,无论你相对于那个点位置,实际偏移的位置都是一样的,因此,从表现上讲,两者最终的位置看上去还是一样的。

您可以狠狠地点击这里:HTML translate和SVG
translate比对demo

图片 14

前面我们提到过,SVG元素也能使用CSS3的transform进行变换(非IE浏览器),但是只能支持2D层面的几个属性,例如translateX(tx),translateY(ty)以及translate(tx[, ty])translateZ(tz)则并不支持。

如果我们使用SVG元素自带的transform属性进行变换,则仅支持translate(tx[ ty])这种用法(缺省使用0代替),当多个参数值的时候,可以使用逗号,或者直接空格分隔,但是不能包含单位,例如下面这种写法直接翘辫子:

CSS

transform=”translate(30px 12px)”

1
transform="translate(30px 12px)"

下面这种无单位写法才可以:

CSS

transform=”translate(30 12)” transform=”translate(30, 12)”

1
2
transform="translate(30 12)"        
transform="translate(30, 12)"

另外,和CSS3的transform一样,SVG中的translate位移也是支持多声明累加的。例如:

CSS

transform=”translate(30 12) translate(30 12)”

1
transform="translate(30 12) translate(30 12)"

等同于:

CSS

transform=”translate(60 24)”

1
transform="translate(60 24)"

需要注意的是,俩个translate中间不要混有其他的transform变换。否则,最终的位移就不是简单的相加了。

Translation

要移动SVG元素,你可以用translate()函数。translate函数的语法如下:

CSS

translate(<tx> [<ty>])

1
translate(<tx> [<ty>])

translate()函数输入一个或两个值得来声明水平和竖直移动值。tx代表x轴上的translation值;ty代表y轴上的translation值。

ty值是可选的,如果省略,默认值为0txty值可以通过空格或者逗号分隔,它们在函数中不代表任何单位-它们默认等于当前用户坐标系单位。

下面的例子把一个元素向右移动100个用户单位,向下移动300个用户单位。

<circle cx=”0″ cy=”0″ r=”100″ transform=”translate(100 300)” />

1
<circle cx="0" cy="0" r="100" transform="translate(100 300)" />

上述代码如果以translate(100, 300)用逗号来分隔值的形式声明一样有效。

图形变换

  在SVG中,坐标变换是对一个坐标系到另一个坐标系的变换的描述

  在2D平面上,一般采用线性变换来满足变换的需求。SVG中的线性变换使用transform属性来完成

  SVG中的transform属性与CSS3中的transform样式里面的一些基本的变换类型是一样的,包括:位移translate,
旋转rotate, 缩放scale,
斜切skew以及直接矩阵matrix。但只局限于2D层面的变换。SVG似乎只支持二维变换,且类似translateXrotateX也都是不支持的

【平移】

  translate()变形方法把元素移动一段距离,可以根据相应的属性定位它

<rect x="0" y="0" width="10" height="10" transform="translate(30,40)" />

  该示例将呈现一个矩形,移到点(30,40),而不是出现在点(0,0)

  [注意]如果没有指定第二个值,它默认被赋值0

图片 15

【旋转】

  使用rotate()变形旋转一个元素

<rect x="20" y="20" width="20" height="20" transform="rotate(45)" />

  该示例显示了一个方形,旋转了45度。rotate()的值是用角度数指定的

图片 16

【缩放】

scale()变形改变了元素的尺寸。它需要两个数字,作为比率计算如何缩放。0.5表示收缩到50%。如果第二个数字被忽略了,它默认等于第一个值

<rect x="20" y="20" width="20" height="20" transform="scale(0.5)" />

图片 17

【斜切】

  在前面的一些变换中,例如位移、缩放之类是不支持translateXscaleX这种CSS常见用法的,但是这里的skew却有点不同:不支持skew(x[, y])这种语法,而只能是skewX或者skewY

  利用一个矩形制作一个斜菱形。可用skewX()变形和skewY()变形。每个需要一角度以确定元素斜切到多远

<rect x="20" y="20" width="20" height="20" transform="skewX(45)" />

【matrix()】

  所有上面的变形可以表达为一个2×3的变形矩阵。组合一些变形,可以直接用matrix(a, b, c, d, e, f)变形设置结果矩阵,利用下面的矩阵,它把来自上一个坐标系统的坐标映射到新的坐标系统:

图片 18

图片 19

【变换列表】

  线性变换列表表示为一系列的变换,结果为变换的矩阵的乘积。要特别注意的是,后面的变换要乘在前面

图片 20

图片 21

 

使用嵌套SVG使元素流动

在保持宽高比的情况下定位元素,我们可以使用嵌套svg只允许特定元素流动-可以不保持这些特定元素的宽高比。

例如,如果你只想SVG中的一个元素流动,你可以把它包含在一个svg中,并且使用preserveAspectRatio="none"来让这个元素扩展始终撑满这个视窗的宽,并且保持宽高比和像我们在之前例子中做的一样定位其他元素。

XHTML

<svg> <!– … –> <svg viewBox=”..”
preserveAspectRatio=”none”> <!– this content will be fluid –>
</svg> <svg viewBox=”..” preserveAspectRatio=”..”> <!–
content positioned somewhere in the viewport –> </svg> <!–
… –> </svg>

1
2
3
4
5
6
7
8
9
10
<svg>
    <!– … –>
    <svg viewBox=".." preserveAspectRatio="none">
        <!– this content will be fluid –>
    </svg>
    <svg viewBox=".." preserveAspectRatio="..">
        <!– content positioned somewhere in the viewport –>
    </svg>
    <!– … –>
</svg>

Jake
Archibald创建了一个简单实用的嵌套SVG使用案例:一个简单的UI可以包含定位在最外层svg角落的元素,并且保持宽高比,UI的中间部分浮动并且根据svg宽度改变进行拉伸。你可以在这里查看。确保你在开发工具里检查代码来选取和想象不同viewbox和svg使用的效果。

三、SVG transform rotate旋转

上面的位移变换,我们似乎没看到明显的不一样。但是,从这里的旋转变换开始,就可以看出明显的差异了。

下面图示的是基本的45度旋转(来自css-tricks)(左HTML元素,右SVG元素):

图片 22

由于SVG元素的默认是SVG左上角为中心变换的,因此,矩形旋转的幅度就有了过山车的感觉。

您可以狠狠地点击这里:HTML rotate和SVG
rotate比对demo

图片 23

结果会发现,两者位置大相径庭了:

CSS transform中的rotate语法比较直白:rotate(angle),就一个angle参数,表示角度大小,不过必须要有单位,比如deg(度),
turn(圈), grad(百分度 –
一种角的测量单位,定义为一个圆周角的1/400。常用于建筑或土木工程的角度测量),甚至可以使用calc()计算,例如:calc(.25turn - 30deg).

但是,SVG中的属性transform旋转就没有这么多花头,单位?哦,别逗了,毛线都没有,直接光秃秃的数值,表示角度deg,例如:

CSS

transform=”rotate(45)”

1
transform="rotate(45)"

具体语法为:rotate(angle[ x y]).
大家注意到没有,这里有个[ x y][]表示这个可选参数。也就是说,SVG中的rotate旋转比CSS的rotate多了一个可选参数,那这个可选参数[ x y]表示什么意思呢?

告诉你,是非常有用的东西。用来偏移transform变换中心点的。

为什么说非常有用呢?SVG元素默认是基于左上角的,但是我们的旋转元素往往都在SVG的中间区域,此时旋转跨度太大,智商余额不足的我们就脑补不过来,此时难免希望可以像CSS的transform变换一样,围绕元素的中心点变换。怎么办?

我们可以借助CSS3 transform-origin修改SVG元素默认的变换中心点,然后配合CSS变换。但是,我们前面多次提到,IE浏览器的SVG元素不识别CSS中的transform.
所以,从兼容性上讲,CSS策略是不可行的。难道就没有其他办法了,有,就是这里的可选参数[ x y],通过对变换中心点的偏移修正,我们也能让SVG元素围绕自身的中心点旋转了。

所以,上面的demo,我们稍微修改下,就能让矩形围绕自己旋转了,见下:

JavaScript

<rect x=”30″ y=”30″ width=”120″ height=”90″ rx=”10″ ry=”10″
fill=”#a0b3d6″ transform=”rotate(45, 90 75)”></rect> <!–
90 = 30 + 120/2 –> <!– 75 = 30 + 90/2 –>

1
2
3
<rect x="30" y="30" width="120" height="90" rx="10" ry="10" fill="#a0b3d6" transform="rotate(45, 90 75)"></rect>
<!– 90 = 30 + 120/2 –>
<!– 75 = 30 +  90/2 –>

您可以狠狠地点击这里:SVG元素也围绕自身中心点旋转demo

图片 24

使用原理图表示就是下面这样(左HTML旋转,右SVG元素偏移旋转):

图片 22

同样的,rotate旋转可以多个值并存,例如下面的CSS和attribute用法:

CSS

transform: rotate(45deg) rotate(-45deg); transform=”rotate(45)
rotate(-45)

1
2
transform: rotate(45deg) rotate(-45deg);
transform="rotate(45) rotate(-45)

然而,需要注意的是,SVG属性的transform声明的中心变换坐标是不能共享的。

因此,虽然transform="rotate(45, 90 75)"是中心点旋转,但是,后面再添加其他东西,例如:rotate(-45)则偏移值忽略,终中心点还是SVG的左上角(0,0)位置,好惨!

例如原来的45度旋转,再加个-45度反向旋转:

XHTML

<rect x=”30″ y=”30″ width=”120″ height=”90″ rx=”10″ ry=”10″
fill=”#a0b3d6″ transform=”rotate(45, 90 75)
rotate(-45)”></rect>

1
<rect x="30" y="30" width="120" height="90" rx="10" ry="10" fill="#a0b3d6" transform="rotate(45, 90 75) rotate(-45)"></rect>

结果位置回去了?才怪呢!图片 26

CSS的是又回去了,但是SVG的确是挂在月球上了。究其原因是rotate(-45)又是相对SVG左上角变换的啦!

图片 27

您可以狠狠地点击这里:SVG连续旋转demo

虽然乍看上去,好像SVG的坐标系统有些怪怪的,但是,实际上,在有些需求场景下,SVG这种看似独立的偏移系统更容易实现一些功能。

比方说,我们希望某个SVG元素先以右下角为中心旋转90度,然后再以右上角为中心旋转90度,该怎么处理?

对于SVG transform,我们直接面向需求写数值就可以了。假设我们的SVG元素的高宽是120*90,
左上角坐标是(30,30), 则,显然,右下角坐标是(150,120),
右上角坐标是(150,30),于是,我们的transform值就很简单:

XHTML

transform=”rotate(90, 150 120) rotate(90 150 30)”

1
transform="rotate(90, 150 120) rotate(90 150 30)"

参见下面的示意图(示意图的坐标与上面略有出入,但不影响原理示意):图片 28

但是,如果我们使用之前容易理解的CSS3来实现,反而就复杂了,因为CSS3中的transform的变换点只能一次性指定,如果要实现不同变换点的旋转效果,只能通过translate再次偏移,例如,实现上面的右下角再右上角90度旋转,则要这样:

CSS

transform-origin: right bottom; /* 或者 100% 100% */ transform:
rotate(90deg) translate(0, -100%) /* 从右下到右上 */ rotate(90deg)
translate(0, 100%);

1
2
3
4
5
6
transform-origin: right bottom; /* 或者 100% 100% */
transform:
  rotate(90deg)
  translate(0, -100%) /* 从右下到右上 */
  rotate(90deg)
  translate(0, 100%);

Gif示意下就是:图片 29

显然要麻烦很多。可见,两种坐标系统没有绝对的优劣。

您可以狠狠地点击这里:右下再右上旋转90度demo

图片 30

上图为两种变换的最终效果,虽然最终的效果是一样的,但是,从理解上而言,这回,是SVG的transform反而更容易理解。还是那句话,辩证看问题,凡事无绝对。

Scaling

你可以通过使用scale()函数变换来向上或者向下缩放来改变SVG元素的尺寸。scale变换的语法是:

CSS

scale(<sx> [<sy>])

1
scale(<sx> [<sy>])

scale()函数输入一个或两个值来声明水平和竖直缩放值。sx代表沿x轴的缩放值,用来水平延长或者拉伸元素;sy代表沿y轴缩放值,用来垂直延长或者缩放元素。

sy值是可选的,如果省略默认值等于sxsxsy可以用空格或者逗号分隔,它们是无单位值。

下面例子把一个元素的尺寸根据最初的尺寸放大两倍:

XHTML

<rect width=”150″ height=”100″ transform=”scale(2)” x=”0″ y=”0″ />

1
<rect width="150" height="100" transform="scale(2)" x="0" y="0" />

下列例子把一个元素缩放到最初宽度的两倍,并且把高度压缩到最初的一半:

XHTML

<rect width=”150″ height=”100″ transform=”scale(2 0.5)” x=”0″ y=”0″
/>

1
<rect width="150" height="100" transform="scale(2 0.5)" x="0" y="0" />

上述例子使用逗号分隔的值例如scale(2, .5)仍然有效。

这里需要注意当SVG元素缩放时,整个坐标系被缩放,导致元素在视窗中重新定位,现在不用担心这些,我们会在下一节中讨论细节。

居中变换

  像缩放、斜切这些SVG变换,想要如CSS transform-origin:50% 50%一样的中心点变换效果,下面有两个思路可供参考

【原始中心位置位于SVG左上角】

  拿45度旋转举例,可以把元素默认就放在中心点和SVG左上角重合的位置上

<rect x="-100" y="-50"  width="200" height="100"/>

图片 31

【viewBox】

  可以把元素默认挂在左上角,然后,通过设置viewBox,让元素呈现的位置并不是真正的左上角,例如应用viewBox='-140 -105 280 210'

图片 32

  此时,只需要让元素旋转就可以了,无需额外的做translate位移

图片 33

  欢迎交流 

 

其他建立新视窗的方法

svg不是唯一能在SVG中创建新视窗的元素。在下面部分,我们会讨论使用其他SVG元素创建新视窗的方法。

四、SVG transform scale缩放

SVG中的缩放的语法就比较单纯了,就scale(sx[, sy])sx表示横坐标缩放比例,sy表示纵坐标缩放比例。其中sy是可缺省的,如果缺失,表示使用和sx一样的值,也就是等比例缩放。其中,sxsy两个参数可以逗号分隔,也可以使用空格分隔。这和CSS3中的使用有所不同,两外,SVG transform属性值缩放是不支持scaleXscaleY这些鬼的。

同样的,CSS控制的transform和SVG元素属性控制的transform的坐标系统是不一样的。一个默认元素中心(下图左),一个是SVG画布左上角(下图右),于是(from
css-tricks):图片 34

因此,当我们对SVG元素scale缩放时候,发现位置也有出乎我们意料,就应该知道是怎么回事了。

rotate旋转虽然也是迥异坐标,但是其参数自带偏移参数,我们我们移个花接个木,还是可以得到我们想要的结果。但是,scale缩放这里,就要悲惨很多了,没有自带偏移参数,于是,当我们要实现SVG元素居中缩放的效果,还需要使用translate手动偏移。

怎么个手动偏移法呢?即使先translate其中心点位置到元素的中心坐标位置,然后缩放,然后坐标再反方向还原回去。例如,某元素中心点坐标是(95, 75),
垂直缩放1.5倍的效果则是:

CSS

transform=”translate(95 75) scale(1, 1.5) translate(-95 -75)”

1
transform="translate(95 75) scale(1, 1.5) translate(-95 -75)"

您可以狠狠地点击这里:CSS transform和SVG transform
scale缩放demo

对应的CSS代码就简单多了,直接:

CSS

.scale { transform: scale(1, 1.5); }

1
2
3
.scale {
    transform: scale(1, 1.5);
}

然后最终效果都是一样的:图片 35

使用Gif原理示意如下:

图片 36

Skew

SVG元素也可以被倾斜,要倾斜一个元素,你可以使用一个或多个倾斜函数skewX 和 skewY

skewX(<skew-angle>) skewY(<skew-angle>)

1
2
skewX(<skew-angle>)
skewY(<skew-angle>)

函数skewX声明一个沿x轴的倾斜;函数skewY声明一个沿y轴的倾斜。

倾斜角度声明是无单位角度的默认是度。

注意倾斜一个元素可能会导致元素在视窗中重新定位。在下一节中有更多细节。

使用<use>ing <symbol>建立一个新的视窗

symbol元素会定义新视窗,无论它什么时候被use元素实例化。

symbol元素的使用可以参考use元素中的xlink:href属性:

XHTML

<svg> <symbol id=”my-symbol” viewBox=”0 0 300 200″> <!–
contents of the symbol –> <!– this content is only rendered when
`use`d –> </symbol> <use xlink:href=”#my-symbol” x=”?”
y=”?” width=”?” height=”?”> </svg>

1
2
3
4
5
6
7
<svg>
    <symbol id="my-symbol" viewBox="0 0 300 200">
        <!– contents of the symbol –>
        <!– this content is only rendered when `use`d –>
    </symbol>
    <use xlink:href="#my-symbol" x="?" y="?" width="?" height="?">
</svg>

上面值中的问号表示这些值也许没有声明-如果xy没有声明,默认值为0,也不需要声明宽高。

看到了吧,当你use一个symbol元素,然后使用开发工具检查DOM,你不会看到use标签中symbol的内容。因为use的内容在shadow
tree里被渲染,如果你在开发工具中允许shadow DOM显示你就能看到。

symbol被使用时,它被深度克隆到生成的shadow
tree中,例外是symbolsvg替换。这个生成的svg总是有明确的宽高。如果宽高的值在use元素上,这些值会被转换生成svg。如果属性宽和/或高没有声明,生成的svg元素会使用这些值的100%。

因为我们在DOM中使用了svg,并且因为这个svg实际上包含在外层svg中,我们遇到的嵌套svg的状况和我们在之前一章讨论到的并没有多少不一样-嵌套的svg形成了一个新的viewport。嵌套svgviewBox是在symbol元素上声明的viewBox。(symbol元素接受viewBox元素值。更多信息,阅读这篇文章:Structuring,
Grouping, and Referencing in SVG –
The , , and Elements)

所以我们现在有了一个新的viewport,尺寸和位置可以使用元素(x,ywidthheight)声明,viewBox值可以在symbol元素上声明。symbol的内容随后再这个视窗和viewBox中被渲染和定位。

最后,symbol元素也接收preserveAspectratio属性值,你可以在由use建立的新视窗中定位viewBox。这很清楚,不是吗?你可以像我们在之前的部分里一样控制新创建的嵌套svg

Dirk
Weber 也创建了一个使用嵌套SVG和symbol元素来模仿CSS
border
images的表现。你可以在这里查看文章。

五、SVG transform skew斜切

先来了解下CSS中的斜切,斜切,如果单纯切一个方向,我们可以看成把矩形变成了平行四边形,其总面积不变化。

以纯X轴变换举例,skewX(-45deg)则下面这样子(灰色方块为原始位置):

图片 37

skewX(45deg)则下面这样子:图片 38

对于SVG的skew斜切变换,表现效果原理是一样的。但是,使用的语法却相当有意思。

在前面的一些变换中,例如位移、缩放之类是不支持translateXscaleX这种CSS常见用法的,但是这里的skew却有点让人哭笑不得:不支持skew(x[, y])这种语法,而只能是skewX或者skewY.

别问我为什么?我只是大自然的搬运工,我不生产语法。

因此,没有:

JavaScript

<del datetime=”2015-10-10T12:49:32+00:00″>transform=”skew(45,
0)”</del>

1
<del datetime="2015-10-10T12:49:32+00:00">transform="skew(45, 0)"</del>

只有:

CSS

transform=”skewX(45)”

1
transform="skewX(45)"

同样的,由于变换中心点的差异,CSS实现的变换和SVG属性变换往往最后的位置是不一样的:

图片 39

不仅如此,如果元素的中心点不是就是SVG的左上角,则skewX(α1) skewX(α2)的最终位置和skewX(α1 + α2)是不一样的(位移和旋转不会这样子)。

您可以狠狠地点击这里:CSS SVG transform
skew斜切差异及连续斜切差异demo

正如demo所示,CSS的和SVG的位置差异很大:图片 40

SVG的连续变换和一次性变换的位置也是不一样的:图片 41

可能有人要疑问,为何连续斜切变换和一次性变换位置会不一样?其实原因很简单,因为斜切的角度和元素偏移大小并不是线性的,比方说,从70到80度和80度到90度之间的位移大小(虽然都是10度的变化区间)是显然不是一个档次的。因此,分开多次连续斜切最终的坐标偏移要比一次性偏移来得小。

最后,和缩放一样,你想要让SVG元素中心点斜切,可以先translate偏移,在skew变换。就不重复举例演示了。

Rotation

你可以使用rotate()函数来旋转SVG元素。这个函数的语法如下:

CSS

rotate(<rotate-angle> [<cx> <cy>])

1
rotate(<rotate-angle> [<cx> <cy>])

rotate()函数对于给定的点和 旋转角度值执行旋转。不像CSS3中的旋转变换,不能声明除degress之外的单位。角度值默认无单位,默认单位是度。

可选的cxcy值代表无单位的旋转中心点。如果没有设置cxcy,旋转点是当前用户坐标系的原点(查看第一部分如果你不知道用户坐标系是什么。)

在函数rotate()中声明旋转中心点一个快捷方式类似于CSS中设置transform: rotate()transform-origin。SVG中默认的旋转中心是当前使用的用户坐标系的左上角,这样也许你无法创建想要的旋转效果,你可以在rotate()中声明一个新的中心点。如果你知道元素在SVG画布中的尺寸和定位,你可以把它的中心设置为旋转中心。

下面的例子是以当前用户坐标系中的(50,50)点为中心进行旋转一组元素:

XHTML

<g id=”parrot” transform=”rotate(45 50 50)” x=”0″ y=”0″> <!–
elements making up a parrot shape –> </g>

1
2
3
<g id="parrot" transform="rotate(45 50 50)" x="0" y="0">
    <!– elements making up a parrot shape –>
</g>

然而,如果你想要一个元素围绕它的中心旋转,你也许想要像CSS中一样声明中心为50% 50%;不幸的是,在rotate()函数中这样做是不允许的-你必须用绝对坐标。然而,你可以在CSS的transform属性中使用transform-origin属性。这篇文章后面会讨论更多细节。

参考<image>中的SVG image建立一个新视窗

images元素表明整个文件的内容被渲染到一个当前用户坐标系中给定的长方形。image元素可以代表图片文件例如PNG或JPEG或者有”image/svg+xml”的MIME类型的文件。

代表SVG文件的image元素会导致建立一个临时新视窗因为定义相关资源有svg元素。

XHTML

<image xlink:href=”myGraphic.svg” x=”?” y=”?” width=”?” height=”?”
preserveAspectRatio=”?” />

1
<image xlink:href="myGraphic.svg" x="?" y="?" width="?" height="?" preserveAspectRatio="?" />

<image>元素接收许多属性,其中一些属性-和这篇文章有关的-是xy位置属性,widthheight属性以及preserveAspectratio

通常,SVG文件会包含一个根<svg>元素;这个元素也许声明位置和尺寸,另外也许有viewBoxpreserveAspectratio值。

当一个image元素代表SVG图片文件,根svg的xywidthheight属性被忽略。除非image元素上的preserveAspectRatio值以“defer”开头,根元素上的preserveAspectRatio值在代表SVG图片时也被忽略。然而相关image元素上的preserveAspectRatio属性定义SVG图片内容如何适应视窗。

评估被参考内容定义的preserveAspectRatio属性时使用viewBox属性值。对于明确定义的viewBox内容(例如,最外层元素上有viewBox属性的SVG文件)值应该被使用。对于大多数值(PING,JPEG),图片边界应该被使用(即image元素有隐含的尺寸为’0
0 raster-image-width
raster-image-height’的viewBox)。如果值不全的话(例如,外层的svg元素没有viewbox属性的SVG文件)preserveAspectRatio值被忽略,只有视窗x & y属性引起的移动才用来显示内容。

例如,如果一个image元素代表PNG或JPEG并且preserveAspectRatio="xMinYMin meet",那么栅格的宽高比会保持,栅格会在保证整个栅格适应视窗的情况下尽可能放大尺寸,栅格的左上角会和由image元素上x,y,widthheight定义的视窗的左上角对齐。

如果preserveAspectRatio的值是“none”那么图片的宽高比不会保持不变。图片会自适应,栅格的左上角和坐标系(x,y)完全对齐,栅格的右下角和坐标系(x+widthy+height)完全对齐。

六、其他居中变换处理

像缩放、斜切这些SVG变换,想要如CSS transform-origin:50% 50%一样的中心点变换效果,需要事先位移,我们有没有其他办法呢?

这里有两个思路可供大家参考下。

1. 原始中心位置乃SVG左上角
拿45度旋转举例,我们可以把元素默认就放在中心点和SVG左上角重合的位置上,参见下面的gif演示:图片 36

于是,我们原来的3步曲就变成了2步曲:

CSS

translate(140 105) rotate(45) translate(-140 -105) → translate(140 105)
rotate(45)

1
translate(140 105) rotate(45) translate(-140 -105) → translate(140 105) rotate(45)

2. 通过viewBox调整
viewBox可以用来改变SVG画布的视区,这个我之前专门撰文介绍过,是SVG学习必备被深入理解的基础知识,参见:“理解SVG的viewport,viewBox,preserveAspectRatio”一文。

我们可以把元素默认挂在左上角,然后,通过viewBox做手脚,让元素呈现的位置并不是真正的左上角,例如应用viewBox='-140 -105 280 210',则变化如下示意图:

图片 43

此时,我们只需要让元素旋转就可以了,无需额外的做translate位移,见下gif:图片 44

坐标系变化

现在我们已经讨论了所有可能的SVG变换函数,我们深入挖掘视觉部分和对SVG元素添加每个变换的效果。这是SVG变换最重要的部分。因此它们被称为“坐标系统变换”而不仅仅是“元素变换”。

在这个说明中,transform属性被定义成两个在被添加的元素上建立新用户空间(当前坐标系)之一-viewBox属性是创建新用户空间的两个属性中的另一个。所以到底是什么意思呢?

这个行为类似于在HTML元素上添加CSS变换-HTML元素坐标系发生了变换,当你把变换组合使用时最明显。虽然在很多方面很相似,HTML和SVG的变换还是有一些不同。

主要的不同是坐标系。HTML元素的坐标系建立在元素自身智商。然而,在SVG中,元素的坐标系最初是当前坐标系或使用中的用户空间。

当你在一个SVG元素上添加transform属性,元素获取当前使用的用户坐标系的一个“副本”。你可以当做给发生变换的元素创建一个新“层”,新层上是当前用户坐标系的副本(the viewBox)。

然后,元素新的当前坐标系被在transform属性中声明的变换函数改变,因此导致元素自身的变换。这看起来好像是元素在变换后的坐标系中重新绘制。

要理解如何添加SVG变换,让我们从可视化的部分开始。下面图片是我们要研究的SVG画布。

图片 45

鹦鹉和小狗使我们要变换的元素(组<g>)。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <g
id=”parrot”> <!– shapes and paths forming the parrot –>
</g> <g id=”dog”> <!– shapes and paths forming the dog
–> </g> </svg>

1
2
3
4
5
6
7
8
<svg width="800" height="800" viewBox="0 0 800 600">
    <g id="parrot">
        <!– shapes and paths forming the parrot –>
    </g>
    <g id="dog">
        <!– shapes and paths forming the dog –>
    </g>
</svg>

灰色坐标是通过viewBox建立的画布的初始坐标系。为了方便起见,我将不改变初始坐标系-我用一个和视窗相同尺寸的viewBox,如你在上述代码中看到的一样。

现在我们建立了画布和初始用户空间,让我们开始变换元素。首先让我们把鹦鹉向左移动150单位,向下移动200个单位。

当然,鹦鹉是由若干路径和形状组成的。只要把transform属性添加到包含它们的组<g>上就行了;这会对整个形状和路径添加变换,鹦鹉会作为一个整体进行变换。查看 article
on structuring and grouping
SVGs获取更多信息。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <g
id=”parrot” transform=”translate(150 200)”> <!– shapes and paths
forming the parrot –> </g> <!– … –> </svg>

1
2
3
4
5
6
<svg width="800" height="800" viewBox="0 0 800 600">
    <g id="parrot" transform="translate(150 200)">
        <!– shapes and paths forming the parrot –>
    </g>
    <!– … –>
</svg>

下面图片展示了上述变换后的结果。鹦鹉的半透明版本是变换前的初始位置。图片 46

SVG中的变换和HTML元素上CSS中的一样简单直观。我们之前提到在元素上添加transform属性时会在元素上创建一个新的当前用户坐标系。下面图片显示了初始坐标系的“副本”,它在鹦鹉元素发生变换时被建立。注意观察鹦鹉当前坐标系是如何变换的。图片 47

这里需要注意的非常重要的一点是建立在元素上的新的当前坐标系是初始用户坐标系的复制,在里面元素的位置得以保持。这意味着它不是建立在元素边界盒上,或者新的当前坐标系的尺寸受制于元素的尺寸。这就是HTML和SVG坐标系之间的区别。

建立在变换元素上的新当前坐标系不是建立在元素边界盒上,或者新的当前坐标系的尺寸受制于元素的尺寸。

我们把小狗变换到画布的右下方时会更加明显。试想我们想要把小狗向右移动50单位,向下移动50单位。这就是狗的最初的坐标以及新的当前坐标系(也因为狗改变)会如何显示。注意小狗的新的坐标系统的原点不在狗边界盒子的左上角。另外注意狗和它新的坐标系看起来它们好像移动到画布新的一层上。图片 48

现在我们试一试其他事情。不再移动,试着缩放。我们将鹦鹉放大到两倍尺寸:

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <g
id=”parrot” transform=”scale(2)”> <!– shapes and paths forming
the parrot –> </g> <!– … –> </svg>

1
2
3
4
5
6
<svg width="800" height="800" viewBox="0 0 800 600">
    <g id="parrot" transform="scale(2)">
        <!– shapes and paths forming the parrot –>
    </g>
    <!– … –>
</svg>

放缩SVG元素和放缩HTML元素的结果不一样。缩放后SVG元素的在视窗中的位置随着缩放改变。下面图片展示了把鹦鹉放大到两倍时的结果。注意初始位置和尺寸,以及最终位置和尺寸。图片 46

从上面图片中我们可以注意到不只鹦鹉的尺寸(宽和高)变成了两倍,鹦鹉的坐标(xy)也乘以了缩放因子(这里是两倍)。

这个结果的原因我们之前已经提到了:元素当前坐标系发生变化,鹦鹉在新系统中绘制。所以,在这个例子中,当前坐标系被缩放。这个效果类似于使用viewBox = "0 0 400 300",等于“放大”了坐标系,因此把里面的内容放大到双倍尺寸(如果你还没有读过请查看这个系列的第一部分)。

所以,如果我们把坐标系变换形象化来展现当前变换系统中的鹦鹉,我们会得到以下结果:图片 50

鹦鹉的新的当前坐标系统被缩放,同时“放大”鹦鹉。注意,在它当前的坐标系中,鹦鹉没有重新定位-只有缩放坐标系统才会导致它在视窗中重定位。鹦鹉在新的缩放后的系统中按初始的xy坐标被重绘。

让我们尝使用不同因子在两个方向上缩放鹦鹉。如果我们添加transform="scale(2 0.5)缩放鹦鹉,我们把宽度变为两倍高度为原来的一半。效果和添加viewBox="0 0 400 1200"类似。图片 51

注意一下鹦鹉在倾斜后的坐标系中的位置,并且把它和初始系统(半透明的鹦鹉)中的位置做比较:xy位置坐标保持不变。

在SVG中倾斜元素也导致元素被“移动”,因为它当前的坐标系统被倾斜了。

试想我们使用skewX函数沿x轴给一只狗增加一个倾斜变化。我们在垂直方向上把狗倾斜了25度。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <!– …
–> <g id=”dog” transform=”skewX(25)”> <!– shapes and paths
forming the dog –> </g> </svg>

1
2
3
4
5
6
<svg width="800" height="800" viewBox="0 0 800 600">
    <!– … –>
    <g id="dog" transform="skewX(25)">
        <!– shapes and paths forming the dog –>
    </g>
</svg>

下列图片展示了对小狗添加倾斜变换的结果。图片 52

注意到狗的位置对比初始位置也改变了,因为它的坐标系也被倾斜了。

下面的图片展示了同样角度的情况下使用skewY()而不是skewX倾斜狗的情况:图片 53

最后,让我们尝试旋转鹦鹉。旋转默认的中心是当前用户坐标系的左上角。新的建立在旋转元素上的当前系统也被旋转了。在下面的例子中,我们将把鹦鹉旋转45度。旋转方向为顺时针。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <g
id=”parrot” transform=”rotate(45)”> <!– shapes and paths forming
the parrot –> </g> <!– … –> </svg>

1
2
3
4
5
6
<svg width="800" height="800" viewBox="0 0 800 600">
    <g id="parrot" transform="rotate(45)">
        <!– shapes and paths forming the parrot –>
    </g>
    <!– … –>
</svg>

添加上述变换的结果如下:图片 54

你很可能想要围绕默认坐标系原点之外的点来旋转一个元素。在transform属性中使用rotate()函数,你可以声明这个点。试想在这个例子中我们想按照它自己的中心旋转这个鹦鹉。根据鹦鹉的宽、高以及位置,我精确计算出它的中心在(150,170)。这个鹦鹉可以围着它的中心旋转。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <g
id=”parrot” transform=”rotate(45 150 170)”> <!– shapes and paths
forming the parrot –> </g> <!– … –> </svg>

1
2
3
4
5
6
<svg width="800" height="800" viewBox="0 0 800 600">
    <g id="parrot" transform="rotate(45 150 170)">
        <!– shapes and paths forming the parrot –>
    </g>
    <!– … –>
</svg>

在这个时候,这只鹦鹉会被旋转并且看起来如下:图片 55

我们说变换添加在坐标系上,因此,元素最终被影响并且发生变换。那么究竟如何改变旋转中心工作在坐标系的原点(0,0)的点呢?

当你改变中心或者旋转时,坐标系被变换或者旋转特定角度,然后再次根据声明的旋转中心产生特定变换。在这个例子中:

XHTML

<g id=”parrot” transform=”rotate(45 150 170)”>

1
<g id="parrot" transform="rotate(45 150 170)">

被浏览器当成一系列的移动和旋转等同于:

XHTML

<g id=”parrot” transform=”translate(150 170) rotate(45)
translate(-150 -170)”>

1
<g id="parrot" transform="translate(150 170) rotate(45) translate(-150 -170)">

当前坐标系变换到你想要的中心店。然后旋转声明的角度。最终系统被负值变换。上述添加到系统的变换如下:图片 56

在我们进行下一部分讨论嵌套和组合变换前,我想请大家注意建立在变换元素上的当前用户坐标系是独立于建立在其他变换元素之上的其他坐标系的。下列图片展示了建立在狗和鹦鹉上的两个坐标系,以及它们之间是如何保持独立的。图片 57

另外注意每个当前坐标系仍然处于在外层<svg>容器中使用viewBox属性建立的画布的主要坐标系中。任何添加到viewBox上的变换会影响整个画布以及所有里面的元素,不管它们是否建立了自己的坐标系。

例如,以下是把整个画布的用户空间从viewBox="0 0 800 600"改成 viewBox="0 0 600 450"的结果。整个画布被缩放,保持任何添加到独立元素上的变换。图片 58

使用<iframe>建立新视窗

代表SVG文件的iframe元素建立新坐标系的情况类似于上述解释的image元素的情况。iframe元素也可以有x,y,widthheight属性,除了它自身的preserveAspectratio之外。

七、结束语

本文介绍的内容实际上都还是非常基本的。实际SVG应用的时候,可能是多个变换参杂在一起,所以,如果本文介绍的几个基本变换都没搞清楚,到时候,想必是想破脑袋都不明白怎么元素跑这里了,怎么变成这样了!

本文的这些知识点虽然基本,但是相当重要的。再加上,不同的变换方法的语法细节还不一样。有的自带偏移,有的需要手动偏移等等;不同变换的前后位置不同,甚至同一变换分开连续变换和一次性变换的结果都不一样等等;都要求大家要细心耐心阅读。

本文内容和结构参考自:Transforms on SVG
Elements.
但要比原文要精炼很多,同时,每一个变换都有亲自实践认证,因此,从品质上讲,可能还要略高一筹。

对了,矩阵matrix没有细说过,这个可以参考我之前的文章:“理解CSS3
transform中的Matrix(矩阵)”,一脉相承的。

我也是初学者,出错在所难免,欢迎指正!

感谢阅读,欢迎交流!图片 59

1 赞 收藏
评论

图片 60

嵌套和组合变换

很多时候你可能想要在一个元素上添加多个变换。添加多个变换意味着“组合”变换。

当变换组合时,最重要的是意识到,和HTML元素变换一样,当这个系统发生了之前的变换后在添加下一个变换到坐标系中。

例如,如果你要在一个元素上添加旋转,接下来移动,移动变换会根据新的坐标系统,而不是初始的没有旋转时的系统。

下面了例子就是做了这件事。我们先添加旋转,然后沿x轴使用transform="rotate(45 150 170) translate(200)"把鹦鹉移动200个单位。图片 61

取决于最终的位置和变换,你可以根据需要组合变换。总是记住坐标系。

注意当你倾斜一个元素-以及它的坐标系统-坐标系统不再是最初的那个,坐标系不再会按照最初的来计算-它将会是倾斜后的坐标系。简单来说,这意味着坐标系原点不再是90度的角,新的坐标会根据新的角度来计算。

当变换元素的子元素也需要变换时会发生变换嵌套。添加到子元素上的变换会累积父元素上添加的变换和它本身的变换。

所以,效果上来说,嵌套变化类似于组合:唯一区别是不像在一个元素上添加一系列的变化,它自动从父元素上获得变换,最后执行添加在它自身的变换,就像我们在上面添加的变换一样-一个接一个。

这对于你想要根据另外一个元素变换一个元素时尤其有用。例如,试想你想要给小狗的尾巴设定一个动画。这个尾巴是#dog组的后代。

XHTML

<svg width=”800″ height=”800″ viewBox=”0 0 800 600″> <!– …
–> <g id=”dog” transform=”translate(..)”> <!– shapes and
paths forming the dog –> <g id=”head”> <!– .. –>
</g> <g id=”body” transform=”rotate(.. .. ..)”> <path
id=”tail” d=”…” transform=”rotate(..)”> <!– animateTransform
here –> </path> <g id=”legs”> <!– … –>
</g> </g> </g> </svg>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<svg width="800" height="800" viewBox="0 0 800 600">
    <!– … –>
    <g id="dog" transform="translate(..)">
        <!– shapes and paths forming the dog –>
        <g id="head">
            <!– .. –>
        </g>
        <g id="body" transform="rotate(.. .. ..)">
            <path id="tail" d="…" transform="rotate(..)">
                <!– animateTransform here –>
            </path>
            <g id="legs">
                <!– … –>
            </g>
        </g>
    </g>
</svg>

试想我们变换dog组;围绕某一点把它的身体旋转一定角度,然后我们想要再把尾巴旋转一定角度。

当尾巴被旋转后,它从祖先(#body)身上“继承”了变换坐标系,也从祖先(#dog)身上继承了变换坐标系,然后旋转(和#body组一样的旋转)然后在发生自身的旋转。这里添加的一系列变换的效果类似于我们之前在上述组合变换例子中解释的。

所以,你看,在#tail上嵌套变换实际上和组合变换有一样的效果。

使用<foreignObject>建立新视窗

foreignObject元素建立一个新的viewport来渲染这个元素的内容。

foreignObject标签允许你把非SVG内容添加到SVG文件中。通常,foreignObject的内容被认为不同于命名空间。例如,你可以把一些HTML放到SVG元素的中间。

foreignObject接收属性包括xyheightwidth,用来定位对象和调整尺寸,创建用于呈现它里面所引用的内容的范围。

有需要关于foreignObject元素的要说因为它给内容创建了新的viewport。如果你感兴趣,可以查看MDN
entry或者在The
Nitty Gritty Blog上查看Christian
Schaeffer创建的实际使用例子。

使用CSS属性变换SVGs

在SVG2中,transform属性简称transform属性;因为SVG变换已经被引入CSS3变换规范中。后者结合了SVG变化,CSS2
2D变换和CSS 3D变换规范,并且把类似transform-origin 和 3D
transformations引入了SVG。

声明在CSS变换规范中的CSS变换属性可以被添加到SVG元素上。然而,transform属性函数值需要遵循CSS规范中的语法声明:函数参数必须逗号隔开-空格隔开是不允许的,但是你可以在逗号前后引用一两个空格;rotate()函数不接受<cx><cy>值-旋转中心使用transform-origin属性声明。另外,CSS变换函数接受角度和坐标单位,例如角度的rad(radians)和坐标的px,em等。

使用CSS来旋转一个SVG元素看起来如下:

CSS

#parrot { transform-origin: 50% 50%; /* center of rotation is set to
the center of the element */ transform: rotate(45deg); }

1
2
3
4
#parrot {
    transform-origin: 50% 50%; /* center of rotation is set to the center of the element */
    transform: rotate(45deg);
}

SVG元素也可以使用CSS
3D变换在三维空间中变换。依然要注意坐标系,然而,不同于建立在HTML元素上的坐标系。这意味着3D旋转看起来也不同除非改变旋转中心。

CSS

#SVGel { transform: perspective(800px) rotate3d(1, 1, 0, 45deg); }

1
2
3
#SVGel {
    transform: perspective(800px) rotate3d(1, 1, 0, 45deg);
}

因为通过CSS来变换SVG元素非常类似于通过CSS来变换HTML元素-语法层面-在这篇文章中我将跳过这个部分。

另外,在写这篇文章的时候,在一些浏览器中实现一些特性是不可能的。因为浏览器支持改变很快,我建议你实验一下这些属性来决定哪些可以工作哪些不可以,决定什么现在可以用什么不可以。

注意一旦CSS变换可以完全实现在SVG上,我依然建议你使用CSS变换函数语法即使你用transform属性的形式添加变换。也就是说,上面提到的transform属性函数的语法还是有效的。

结束语

建立新的viewports和坐标系-像上述提到的一样通过嵌套svg和其他元素-允许你控制SVG的部分内容而通过其他方式你可能没法一样控制。

在写这片文章以及思考例子和使用情况的整个过程中,我一直在思考嵌套SVG如何让我们在处理SVG时能更好控制并有更灵活的方式。自适应SVG可以通过简洁的代码创建,在SVG中可以创建独立于其他元素的流动元素,用来模拟CSS
border images来在高分屏上定义背景。

你是否已经在SVG中使用嵌套视窗来创建有趣的例子了呢?你能否相处更多有创意的例子呢?

这篇文章总结了“理解SVG坐标系和变换”这个系列。下一步,我们会讨论动画,甚至更多!敬请期待,感谢你的阅读!

1 赞 1 收藏
评论

图片 60

动画transform

SVG变换可以变成动画,就像CSS变换一样。如果你使用CSS transform属性来产生SVG变换,你可以像在HTML元素上创建CSS变换动画一样使用CSS动画把这些变换变成动画。

SVGtransform属性可以用SVG<animateTransform>元素来做成动画。<animateTransform>元素是SVG中三个用来给不同的SVG属性设置动画的元素之一。

关于<animateTransform>元素的详细内容不在本片文章的讨论范围内。阅读我写的关于不同SVG动画元素的文章,包括<animateTransform>

最后的话

学习SVGs一开始可能非常困惑,如果对于坐标系变换里的内容不是很清楚,尤其是如果你带着CSS
HTML变换的背景知识,自然而然希望SVG元素和HTML元素的变换一样。

然而,一旦你意识到它们的工作方式,你能更好得控制SVG画布,并且轻易操纵元素。

这一系列的最后部分,我将讨论嵌套SVGs和建立新的viewports和viewboxes。敬请关注!

1 赞 1 收藏
评论

图片 60

发表评论

电子邮件地址不会被公开。 必填项已用*标注