SVG综述
位图与矢量图
HTML体系中,最常用的绘制矢量图的技术是SVG和HTML5新增加的canvas元素。这两种技术都支持绘制矢量图和光栅图。
SVG简介
可缩放矢量图形(Scalable Vector Graphics,简称SVG)是一种使用XML来描述二维图形的语言(SVG严格遵从XML语法)。 SVG允许三种类型的图形对象:矢量图形形状(例如由直线和曲线组成的路径)、图像和文本。 可以将图形对象(包括文本)分组、样式化、转换和组合到以前呈现的对象中。 SVG 功能集包括嵌套转换、剪切路径、alpha 蒙板和模板对象。
SVG既可以说是一种协议,也可以说是一门语言;既是HTML的一个标准元素,也是一种图片格式。
SVG与其它图片格式的比较
SVG与其它的图片格式相比,有很多优点(很多优点来源于矢量图的优点):
- SVG文件是纯粹的XML, 可被非常多的工具读取和修改(比如记事本)。
- SVG 与JPEG 和GIF图像比起来,尺寸更小,且可压缩性更强。
- SVG 是可伸缩的,可在图像质量不下降的情况下被放大,可在任何的分辨率下被高质量地打印。
- SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)。
- SVG 可以与 Java 技术一起运行。
- SVG 是开放的标准。
SVG与Flash的比较
SVG 的主要竞争者是Flash。与Flash相比,SVG 最大的优势是它与其他标准(比如XSL和DOM)相兼容,操作方便,而Flash则是未开源的私有技术。其它的比如存储的格式,动态生成图形等方面,SVG也占有很大的优势。
浏览器支持情况(http://caniuse.com/#cats=SVG)
√ IE 9+
√ Chrome 33.0+
√ Firefox 28.0+
√ Safari 7.0+
SVG简单案例
- 浏览器直接打开
- 在 HTML 中使用标签引用
效果: - 直接在 HTML 中使用 SVG 标签
效果: - 作为 CSS 背景
效果:
因为我们SVG的尺寸是设置200x200所以这边自动平铺
SVG图形绘制
基本形状
SVG提供了很多的基本形状,这些元素可以直接使用,这一点比canvas好多了。废话不说了,直接看例子,这个最直接:
这段HTML显示的结果如下:
这个例子画了很多形状:正常的矩形,带圆角的矩形,圆形,椭圆形,直线,折线,多边形,还有路径。这些都属于基本的图形元素。虽然绘制一个图形有很多种方式,比如矩形可以用rect实现,也可以用path等实现,但是我们还是应该尽量保持SVG的内容短小精悍,易于阅读。
下面是每个形状的使用说明(这里只介绍控制图形形状和位置的基本属性,填充等属性放到后面总结)。
矩形 - rect元素
这个元素有6个控制位置和形状的属性,分别是:
x:矩形左上角的坐标(用户坐标系)的x值。
y:矩形左上角的坐标(用户坐标系)的y值。
width:矩形宽度。
height:矩形高度。
rx:实现圆角效果时,圆角沿x轴的半径。
ry:实现圆角效果时,圆角沿y轴的半径。
比如上面例子中,实现了圆角效果,你也可以通过设置rx,ry为不同的值实现椭圆角效果。
圆 - circle元素
这个元素的属性很简单,主要是定义圆心和半径:
r:圆的半径。
cx:圆心坐标x值。
cy:圆心坐标y值。
椭圆 - ellipse元素
这个是更加通用的圆形元素,你可以分别控制半长轴和半短轴的长度,来实现不同的椭圆,很容易想到,当两个半轴相等时,就是正圆形了。
rx:半长轴(x半径)。
ry:半短轴(y半径)。
cx:圆心坐标x值。
cy:圆心坐标y值。
直线 - line元素
直线需要定义起点与终点即可:
x1:起点x坐标。
y1:起点y坐标。
x2:终点x坐标。
y2:终点y坐标。
折线 - polyline元素
折线主要是要定义每条线段的端点即可,所以只需要一个点的集合作为参数:
points:一系列的用空格,逗号,换行符等分隔开的点。每个点必须有2个数字:x值和y值。所以下面3个点 (0,0), (1,1)和(2,2)可以写成:"0 0, 1 1, 2 2"。
多边形 - polygon元素
这个元素就是比polyline元素多做一步,把最后一个点和第一个点连起来,形成闭合图形。参数是一样的。
points:一系列的用空格,逗号,换行符等分隔开的点。每个点必须有2个数字:x值和y值。所以下面3个点 (0,0), (1,1)和(2,2)可以写成:"0 0, 1 1, 2 2"。
路径 - path元素
这个是最通用,最强力的元素了;使用这个元素你可以实现任何其他的图形,不仅包括上面这些基本形状,也可以实现像贝塞尔曲线那样的复杂形状;此外,使用path可以实现平滑的过渡线段,虽然也可以使用polyline来实现这种效果,但是需要提供的点很多,而且放大了效果也不好。这个元素控制位置和形状的只有一个参数:
d:一系列绘制指令和绘制参数(点)组合成。
绘制指令分为绝对坐标指令和相对坐标指令两种,这两种指令使用的字母是一样的,就是大小写不一样,绝对指令使用大写字母,坐标也是绝对坐标;相对指令使用对应的小写字母,点的坐标表示的都是偏移量。
绝对坐标绘制指令
这组指令的参数代表的是绝对坐标。假设当前画笔所在的位置为(x0,y0),则下面的绝对坐标指令代表的含义如下所示:
指令 | 参数 | 说明 |
---|---|---|
M | x y | 将画笔移动到点(x,y) |
L | x y | 画笔从当前的点绘制线段到点(x,y) |
H | x | 画笔从当前的点绘制水平线段到点(x,y0) |
V | y | 画笔从当前的点绘制竖直线段到点(x0,y) |
A | rx ry x-axis-rotation large-arc-flag sweep-flag x y | 画笔从当前的点绘制一段圆弧到点(x,y) |
C | x1 y1, x2 y2, x y | 画笔从当前的点绘制一段三次贝塞尔曲线到点(x,y) |
S | x2 y2, x y | 特殊版本的三次贝塞尔曲线(省略第一个控制点) |
Q | x1 y1, x y | 绘制二次贝塞尔曲线到点(x,y) |
T | x y | 特殊版本的二次贝塞尔曲线(省略控制点) |
Z | 无参数 | 绘制闭合图形,如果d属性不指定Z命令,则绘制线段,而不是封闭图形。 |
移动画笔指令M,画直线指令:L,H,V,闭合指令Z都比较简单;下面重点看看绘制曲线的几个指令。
绘制圆弧指令:A rx ry x-axis-rotation large-arc-flag sweep-flag x y
用圆弧连接2个点比较复杂,情况也很多,所以这个命令有7个参数,分别控制曲线的的各个属性。下面解释一下数值的含义:
rx,ry 是弧的半长轴、半短轴长度
x-axis-rotation 是此段弧所在的x轴与水平方向的夹角,即x轴的逆时针旋转角度,负数代表顺时针转动的角度。
large-arc-flag 为1 表示大角度弧线,0 代表小角度弧线。
sweep-flag 为1代表从起点到终点弧线绕中心顺时针方向,0 代表逆时针方向。
x,y 是弧终端坐标。
前两个参数和后两个参数就不多说了,很简单;下面就说说中间的3个参数:
x-axis-rotation代表旋转的角度,体会下面例子中圆弧的不同:
上面的HTML画出下面的图形:
从图中可以看到椭圆旋转参数的不同导致绘制的圆弧方向不同,当然这个参数对正圆来说没有影响。
large-arc-flag和sweep-flag控制了圆弧的大小和走向,体会下面例子中圆弧的不同:
这个HTML绘制了下面的几幅图:
从上面可以看出,这几个参数其实是唯一确定一段圆弧需要的参数。这里也看到,SVG中的圆弧比canvas中的复杂一些。
绘制三次贝塞尔曲线指令:C x1 y1, x2 y2, x y
三次贝塞尔曲线有两个控制点,就是(x1,y1)和(x2,y2),最后面(x,y)代表曲线的终点。体会下面的例子:
这个HTML片段绘制了下面的图形:
从上面我们可以看到,控制点控制了曲线的弧度。
特殊版本的三次贝塞尔曲线:S x2 y2, x y
很多时候,为了绘制平滑的曲线,需要多次连续绘制曲线。这个时候,为了平滑过渡,常常第二个曲线的控制点是第一个曲线控制点在曲线另外一边的映射点。这个时候可以使用这个简化版本。这里要注意的是,如果S指令前面没有其他的S指令或C指令,这个时候会认为两个控制点是一样的,退化成二次贝塞尔曲线的样子;如果S指令是用在另外一个S指令或者C指令后面,这个时候后面这个S指令的第一个控制点会默认设置为前面的这个曲线的第二个控制点的一个映射点,体会一下:
这个HTML片段绘制如下图形:
上面的S指令只有第二个控制点,第一个控制点采用了前面的曲线指令的第二个控制点的一个映射点。
绘制二次贝塞尔曲线指令:Q x1 y1, x y , T x y (特殊版本的二次贝塞尔曲线)
二次贝塞尔曲线只有一个控制点(x1,y1),通常如下图所示:
如果是连续的绘制曲线,同样可以使用简化版本T。同样的,只有T前面是Q或者T指令的时候,后面的T指令的控制点会默认设置为前面的曲线的控制点的映射点,体会一下:
同样的,如果T指令前面不是Q或者T指令,则认为无控制点,画出来的是直线。
相对坐标绘制指令
与绝对坐标绘制指令的字母是一样的,只不过全部是小写表示。这组指令的参数中涉及坐标的参数代表的是相对坐标,意思就是参数代表的是从当前点到目标点的偏移量,正数就代表向轴正向偏移,负数代表向反向偏移。不过对Z指令来说,大小写没有区别。
这里要注意,绝对坐标指令与相对坐标指令是可以混合使用的。有时混合使用可以带来更灵活的画法。
SVG path绘制注意事项:
绘制带孔的图形时要注意:外层边的绘制需要是逆时针顺序的,里面的洞的边的顺序必须是顺时针的。只有这样绘制的图形填充效果才会正确。
SVG文本与图像
SVG中渲染文本
SVG的强大能力之一是它可以将文本控制到标准HTML页面不可能有的程度,而无须求助图像或其它插件。任何可以在形状或路径上执行的操作(如绘制或滤镜)都可以在文本上执行。尽管SVG的文本渲染如此强大,但是还是有一个不足之处:SVG不能执行自动换行。如果文本比允许空间长,则简单地将它切断。多数情况下,创建多行文本需要多个文本元素。
此外,可以使用 tspan 元素可以将文本元素分成几部分,允许每部分有各自的样式。
还有,在text元素中,空格的处理与HTML类似:换行和回车变成空格,而多个空格压缩成单个空格。
直接显示在图片中的文本 - text元素
直接显示文本,可以使用text元素,例子如下:
如上面的例子中所示,text元素可以设置下列的属性:
- x,y是文本位置坐标。
- text-anchor是文本显示的方向,其实也就是位置(x,y)处于文本的位置。这个属性有start,middle和end三种值。
- start表示文本位置坐标(x,y)位于文本的开始处,文本从这点开始向右挨个显示。
- middle表示(x,y)位于文本中间处,文本向左右两个方向显示,其实就是居中显示。
- end表示(x,y)点位于文本结尾,文本向左挨个显示。
除了这些属性,下面的这些属性都既可以在CSS中指定,也可以直接在属性中指定:
- fill,stroke:填充和描边颜色,具体使用在后面总结。
- font的相关属性:font-family, font-style, font-weight, font-variant, font-stretch, font-size, font-size-adjust, kerning, letter-spacing, word-spacing and text-decoration。
文本区间 - tspan元素
这个元素是text元素的强力补充;它用于渲染一个区间内的文本;它只能出现在text元素或者tspan元素的子元素中。典型的用法就是强调显示部分文本。例如:
tspan元素有下列的属性可以设置:
- x,y用于设置包含的文本的绝对坐标值,这个值会覆盖默认的文本位置。这些属性可以包含一系列数字,这些数字会应用到每个对应的单个字符。没有对应设置的字符会紧跟前一个字符。例如:
- dx,dy用于设置包含的文本相对于默认的文本位置的偏移量。这些属性同样可以包含一系列数字,每个都会应用到对应的字符。没有对应设置的字符会紧跟前一个字符。你可以把上面的例子中的x换成dx看看效果。
- rotate用于设置字体的旋转角度。这个属性页可以包含一系列数字,应用到每个字符。没有对应设置的字符会使用最后设置的那个数字。
- textLength:这是最令人费解的属性,据说设置完以后,渲染发现文本的长度与这个值不一致时,会以这个长度为准。但是我没有试出来效果。
文本引用 - tref元素
这个元素允许引用定义过的文本,并高效的拷贝到当前位置,通常配合xlink:href指定目的元素。因为是拷贝过来的,所以使用css修改当前文本的时候,不会修改原来的文本。看例子:
文本路径 - textPath元素
这个比较有意思,效果也很酷,能做出很多的艺术效果;这个元素从它的xlink:href属性获取指定的路径并把文本对齐到这个路径上,看例子:
SVG中渲染图片 - image元素
SVG中的image元素可以直接支持显示光栅图片,使用很简单。看下面的例子:
这里需要注意几点:
- 如果没有设置x或y坐标,则默认是0。
- 如果没有设置width或height,则默认也是0.
- 如果显式的设置width或height为0,则会禁止渲染这幅图片。
- 图片的格式支持png,jpeg,jpg,svg等等,所以svg是支持嵌套svg的。
- image与其他元素一样,是svg的常规元素,所以它支持所有的裁剪,蒙板,滤镜,旋转等效果。
SVG笔画与填充
前面我们重点都在总结各类形状,文本和图片,接下来,我们还是和讨论canvas一样,总结一下颜色处理,也就是填充和边框效果;你会发现这里的内容与canvas基本上是一致的。这些属性既可以以属性的形式写在元素中,也可以以CSS的形式保存(这是与canvas不一样的地方)。
填充色 - fill属性
这个属性使用设置的颜色填充图形内部,使用很简单,直接把颜色值赋给这个属性就可以了。看例子:
上面例子中画了一个红色蓝边的矩形。注意几点:
- 如果不提供fill属性,则默认会使用黑色填充,如果要取消填充,需要设置成none。
- 可以设置填充的透明度,就是fill-opacity,值的范围是0到1。
- 稍微复杂一点的是fill-rule属性。这个属性定义了判断点是不是属于填充范围的算法;除了inherit这个值外,还有两个取值:
nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:
evenodd:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则改点在图形内,需要填充;个数为偶数则点在图形外,不需要填充。看下图的示例:
边框色 - stroke属性
上面的例子中已经用到了stroke属性,这个属性使用设置的值画图形的边框,使用起来也很直接,把颜色值赋给它就可以了。注意:
1. 如果不提供stroke属性,则默认不绘制图形边框。
2. 可以设置边的透明度,就是stroke-opacity,值的范围是0到1。
实际上,边的情况比图形内部稍微复杂一点,因为边除了颜色,还有"形状"需要定义。
线的端点 - stroke-linecap属性
这个属性定义了线段端点的风格,这个属性可以使用butt,square,round三个值。看例子:
这段代码绘制了3条使用不同风格线端点的线,从图中我们可以很容易看出3中风格的不同。
线的连接 - stroke-linejoin属性
这个属性定义了线段连接处的风格,这个属性可以使用miter,round,bevel三个值。看例子:
从上面的图中我们很容易看到3中风格的不同。
线的虚实 - stroke-dasharray属性
这个属性可以设置线段采用何种虚实线。看例子:
这个属性是设置一些列数字,不过这些数字必须是逗号隔开的。属性中当然可以包含空格,但是空格不作为分隔符。每个数字定义了实线段的长度,分别是按照绘制、不绘制这个顺序循环下去。所以上面的例子中绘制的线是画5单位的实线,留5单位的空格,再画5单位的实线...这样一直下去。
除了这些常用的属性,还有下列属性可以设置:
stroke-miterlimit:这个和canvas中的一样,它处理什么时候画和不画线连接处的miter效果。
stroke-dashoffset:这个属性设置开始画虚线的位置。
使用CSS展示数据
HTML5强化了DIV+CSS的思想,所以展示数据的部分还可以交给CSS处理。与普通HTML元素相比,只不过是 background-color和border换成了fill和stroke。其他的大多都差不多。简单看个例子:
是不是很熟悉,就是这么简单的。
SVG颜色的表示
SVG和canvas中是一样的,都是使用标准的HTML/CSS中的颜色表示方法,这些颜色都可以用于fill和stroke属性。
基本有下面这些定义颜色的方式:
- 颜色名字: 直接使用颜色名字red, blue, black...
- rgba/rgb值: 这个也很好理解,例如#ff0000,rgba(255,100,100,0.5)。
- 十六进制值: 用十六进制定义的颜色,例如#ffffff。
- 渐变值:这个也与canvas中一样,支持两种渐变色:线性渐变,环形渐变。如下图所示:
- 图案填充:使用自定义的图案作为填充色。
前面几种都很简单,重点看下后面两种填充色。
线性渐变
使用linearGradient元素即可定义线性渐变,每一个渐变色成分使用stop元素定义。看下面的例子:
从上面的例子看到,除了元素名字和一些特别的成员,其他的所有都和线性渐变一样,包括stop的定义,必须放到defs中,必须给它设置id,使用url(#id)去赋值等。这些特别的成员如下:
offset属性:这个和线性渐变的值是一样,但是含义不一样。在环形渐变中,0%代表圆心处,这个很好理解。
cx,cy,r属性:其实也很好理解,环形渐变,当然要定义环的圆心和半径了,体会一下上面例子中圆的大小和位置就能理解了。
fx,fy属性:定义颜色中心(焦点)处的位置,也就是渐变色最浓处的坐标,在上面例子中,红色最红的是圆心,这是默认效果;如果想改变一下,就可以设置fx,fy坐标值。
不过这里需要注意一下上面cx,cy,r,fx,fy的值,你会发现它们都是小数,那么单位是什么呢?
这个需要先了解另外一个相关的属性:gradientUnits,它定义了定义渐变色使用的坐标单位。这个属性有2个可用值:userSpaceOnUse和objectBoundingBox。
objectBoundingBox是默认值,它使用的坐标都是相对于对象包围盒的(方形包围盒,不是方形包围盒的情况比较复杂,略过),取值范围是0到1。例如上例中的cx,cy的坐标值(0.25,0.25)。意味着这个圆心是在包围盒的左上角1/4处,半径0.25意味着半径长是对象方形包围盒长的1/4,就像你们图中看到的那样。
userSpaceOnUse表示使用的是绝对坐标,使用这个设置的时候,你必须要保证渐变色和填充的对象要保持在一个位置。
再看下面这个例子,注意gradientUnits属性默认值是objectBoundingBox:
此外,还有渐变色元素还有一些变换的属性,如gradientTransform,这个不是这里的重点,后面会总结变换。
另外一个可能用到的属性是spreadMethod属性,这个属性定义了渐变色到达它的终点时应该采取的行为。该属性有3个可选值:pad(默认值),reflect,repeat。pad不用说了,属于自然过渡,渐变色结束以后,使用最后一个成员色直接渲染对象剩下的部分。refect会让渐变色继续,只不过渐变色会反向继续渲染,从最后一个颜色开始到第一个颜色这个顺序渲染;等到再次到达渐变色终点时,再反序,如此这般指导对象填充完毕。repeat也会让渐变色继续渲染,但是不会反序,还是一遍一遍从第一种颜色到最后一种颜色渲染。效果图如下所示:
看一段重复渲染的代码:
纹理填充
纹理填充也是一种流行的填充方式,在SVG中,可以使用pattern创建一个纹理,然后用这个pattern去填充别的对象。直接看例子:
例子看起来很简单,由渐变色创建pattern,然后使用pattern
填充矩形。这里需要注意:
1.不同的浏览器填充这个pattern的时候效果不一样。
2.pattern也需要定义id。
3.pattern也必须要定义在defs中。
4.pattern的使用也是把url(#id)直接赋值给fill或stroke。
上面这些都是很简单的,我们重点看一下例子中的坐标表示情况,坐标在pattern中比较复杂。
pattern中包含两个相关属性:patternUnits和patternContentUnits属性;这两个属性的取值都还是只有2个:objectBoundingBox和userSpaceOnUse,这两个值的含义上面以及讲过了。这里容易混淆的是这两个属性的默认值不同,但是当你理解这么做的原因以后,你又会发现这么做还真是有道理。
patternUnits属性
这个属性与Gradient的gradientUnits属性是一样的,默认采用objectBoundingBox。受这个属性影响的属性有x,y,width,height,这4个属性分别定义了pattern的起点,宽高度。它们都采用了相对值,例子中想要在水平和竖直方向上都填充4次,所以width和height都设为了0.25。
patternContentUnits属性
这个属性的默认值正好相反,采用userSpaceOnUse。这个属性描述了pattern中绘制的形状(比如上面的rect,circle)的坐标系统。也就是说在默认情况下,你在pattern中绘制的形状和pattern自身的大小/位置使用了不一样的坐标系。考虑上面例子中的情况,我们想填充一个200*200的矩形,而且每个方向重复4次。这就意味着每个pattern是50*50的,那么pattern里面的两个矩形和一个圆形就是画在这个50*50的矩形中。这样我们就能理解上面pattern中的矩形和圆的坐标了。此外,这个例子中的pattern为了居中,需要偏移10px后开始渲染,而这个值是受patternUnits属性制约的,所以默认情况下,x,y值就为:10/200=0.05。
SVG坐标与变换
坐标系统
SVG存在两套坐标系统:视窗坐标系与用户坐标系。默认情况下,用户坐标系与视窗坐标系的点是一一对应的,都为原点在视窗的左上角,x轴水平向右,y轴竖直向下;如下图所示:
SVG的视窗位置一般是由CSS指定,尺寸由SVG元素的属性width和height设置,但是如果SVG是存储在embedded对象中(例如object元素,或者其他SVG元素),而且包含SVG的文档是用CSS或者XSL格式化的,并且这些外围对象的CSS或者其他指定尺寸的值已经可以计算出视窗的尺寸了,则此时会使用外围对象的尺寸。
这里需要区分视窗,视窗坐标系,用户坐标系的概念:
视窗:指的是网页上面可视的矩形局域,长度和宽度都是有限的,这个区域一般与外围对象的尺寸有关。
视窗坐标系:本质是一个坐标系,有原点,x轴与y轴;而且在两个方向上是无限延伸的。默认情况下,原点在视窗的左上角,x轴水平向右,y轴竖直向下。可以对这个坐标系的点进行变换。
用户坐标系:本质是一个坐标系,有原点,x轴与y轴;而且在两个方向上是无限延伸的。默认情况下,原点在视窗的左上角,x轴水平向右,y轴竖直向下。可以对这个坐标系的点进行变换。
默认情况下,视窗坐标系与用户坐标系是重合的,但是这里需要注意,视窗坐标系属于的是创建视窗的元素,视窗坐标系确定好以后,整个视窗的坐标基调就确定了。但是用户坐标系是属于每个图形元素的,只要图形进行了坐标变换,就会创建新的用户坐标系,这个元素中所有的坐标和尺寸都使用这个新的用户坐标系。
简单点说:视窗坐标系描述了视窗中所有元素的初始坐标概况,用户坐标系描述了每个元素的坐标概况,默认情况下,所有元素都使用默认的与视窗坐标系重合的那个用户坐标系。
坐标空间变换
让我们回顾一下canvas用户坐标的变换,它们是通过平移,缩放,旋转函数实现的;每次变换后对以后绘制的图形都起作用,除非再次进行变换,这是"当前"用户坐标系统的概念。canvas只有唯一一个用户坐标系。
在SVG中,情况完全不同。SVG本身作为一种向量图元素,它的两个坐标系统本质上都可以算作"用户坐标系统";SVG的两个坐标空间都是可以变换的:视窗空间变换和用户空间变换。视窗空间变换由相关元素(这些元素创建了新的视窗)的属性viewBox控制;用户空间变换由图形元素的transform属性控制。视窗空间变换应用于对应的整个视窗,用户空间变换应用于当前元素及其子元素。
视窗变换 - viewBox属性
所有的能建立一个视窗的元素,再加上marker,pattern,view元素,都有一个viewBox属性。
viewBox属性值的格式为(x0,y0,u_width,u_height),每个值之间用逗号或者空格隔开,它们共同确定了视窗显示的区域:视窗左上角坐标设为(x0,y0)、视窗的宽设为u_width,高为u_height;这个变换对整个视窗都起作用。
这里一定不要混淆:视窗的大小和位置已经由创建视窗的元素和外围的元素共同确定了(例如最外层的svg元素建立的视窗由CSS,width和height确定),这里的viewBox其实是设置这个确定的区域能显示视窗坐标系的哪个部分。
viewBox的设置其实是包含了视窗空间的缩放和平移两种变换。
变换的计算也很简单:以最外层的svg元素的视窗为例,假设svg的宽与长设置为width,height,viewBox的设置为(x0,y0,u_width,u_height)。则绘制的图形,宽和高的缩放比例分别为:width/u_width, height/u_height。视窗的左上角的坐标设置为了(x0,y0)。
体会下面几种代码绘出的结果的不同:
上面的例子绘制的图中你可以看到绿色和红色的矩形,这种情况下视窗坐标系的点还是与视窗上的点是一一对应的,这个也是默认情况。
上面的例子绘制的图中这个你只能看到绿色的矩形,而且绿色的矩形显示在屏幕上是200*200像素的,这个时候坐标点已经不是一一对应了,图被放大了。
上面的例子绘制的图中,视窗坐标系的单位被缩小,所以两个矩形都缩小了。
在日常工作中,我们经常需要完成的一个任务就是缩放一组图形,让它适应它的父容器。我们可以通过设置viewBox属性达到这个目的。
能建立新视窗的元素
任何时候,我们都可以嵌套视窗。创建新的视窗的时候,也会创建新的视窗坐标系和用户坐标系,当然也包括裁减路径也会创建新的。下列是能建立新视窗的元素列表:
svg:svg支持嵌套。
symbol:当被use元素实例化的时候创建新的视窗。
image:引用svg元素时会创建新视窗。
foreignObject:创建新视窗去渲染里面的对象。
保持缩放的比例 - preserveAspectRatio属性
有些时候,特别是当使用viewBox的时候,我们期望图形占据整个视窗,而不是两个方向上按相同的比例缩放。而有些时候,我们却是希望图形两个方向是按照固定的比例缩放的。使用属性preserveAspectRatio就可以达到控制这个的目的。
这个属性是所有能建立一个新视窗的元素,再加上image,marker,pattern,view元素都有的。而且preserveAspectRatio属性只有在该元素设置了viewBox以后才会起作用。如果没有设置viewBox,则preserveAspectRatio属性会被忽略。
属性的语法如下:preserveAspectRatio="[defer] <align> [<meetOrSlice>]"
注意3个参数之间需要使用空格隔开。
defer:可选参数,只对image元素有效,如果image元素中preserveAspectRatio属性的值以"defer"开头,则意味着image元素使用引用图片的缩放比例,如果被引用的图片没有缩放比例,则忽略"defer"。所有其他的元素都忽略这个字符串。
align:该参数决定了统一缩放的对齐方式,可以取下列值:
none - 不强制统一缩放,这样图形能完整填充整个viewport。
xMinYMin - 强制统一缩放,并且把viewBox中设置的<min-x>和<min-y>对齐到viewport的最小X值和Y值处。
xMidYMin - 强制统一缩放,并且把vivewBox中X方向上的中点对齐到viewport的X方向中点处,简言之就是X方向中点对齐,Y方向与上面相同。
xMaxYMin - 强制统一缩放,并且把viewBox中设置的<min-x> + <width>对齐到viewport的X值最大处。
类似的还有其他类型的值:xMinYMid,xMidYMid,xMaxYMid,xMinYMax,xMidYMax,xMaxYMax。这些组合的含义与上面的几种情况类似。
meetOrSlice:可选参数,可以去下列值:
meet - 默认值,统一缩放图形,让图形全部显示在viewport中。
slice - 统一缩放图形,让图形充满viewport,超出的部分被剪裁掉。
下图诠释了各种填充的效果:
用户坐标系的变换 - transform属性
该类型变换是通过设置元素的transform属性来指定的。这里需要注意,transform属性设置的元素的变换,只影响该元素及其子元素,与别的元素无关,不影响别的元素。
平移 - translate
平移变换把相关的坐标值平移到指定的位置,该变换需要传入两个轴上平移的量。看例子:
这个例子绘制一个矩形,并把它的起点(0,0)平移到(30,40)处。虽然可以直接设置(x,y)的坐标值,但是使用平移变换去实现也很方便。这个变换第二个参数可以省略,默认当0处理。
旋转 - rotate
旋转一个元素也是一个很常见的任务,我们可以使用rotate变换实现,该变换需要传入旋转的角度参数。看例子:
这个例子会显示一个旋转45度的矩形。有几点注意:
1.这里的变换是以角度值为参数的。
2.旋转指的是相对于x轴的旋转。
3.旋转是围绕用户坐标系的原点(0,0)展开的。
倾斜 - skew
transform还支持倾斜变换,可以是沿着x轴的(左右倾斜,正角度为向右倾斜,其实是倾斜了y轴),或者是沿着y轴的(上下倾斜,正角度为向下倾斜,其实是倾斜了x轴)倾斜;该变换需要传入一个角度参数,这个角度参数会决定倾斜的角度。看下面的例子:
从结果中,你可以直接看到同样尺寸的矩形,在不同的倾斜变换后,得到的位置和形状。这里注意矩形的起始位置都已经改变了,这是因为在新的坐标系统中,(30,30)已经在不同的位置了。
缩放 - scale
缩放对象由缩放变换完成,该变换接受2个参数,分别指定在水平和竖直上的缩放比例,如果第二个参数省略则与第一个参数取相同的值。看下面的例子:
变换矩阵 - matrix
学过图形学的都知道,所有的变换其实都是由矩阵表征的,所以上面的变换其实都可以用一个3*3矩阵去表示:
a c e b d f 0 0 1
由于只有6个值用到了,所以也简写成[a b c d e f]。把matrix(a,b,c,d,e,f)赋给transfrom就可以实施相应的变换。变换会把坐标和长度都转换成新的尺寸。上面各种变换对应的矩阵如下:
平移变换[1 0 1 0 tx ty]:
1 0 tx 0 1 ty 0 0 1
缩放变换[sx 0 0 sy 0 0]:
sx 0 0 0 sy 0 0 0 1
旋转变换[cos(a) sin(a) -sin(a) cos(a) 0 0]:
cos(a) -sin(a) 0 sin(a) cos(a) 0 0 0 1
沿X轴的倾斜[1 0 tan(a) 1 0 0]:
1 tan(a) 0 0 1 0 0 0 1
沿Y轴的倾斜[1 tan(a) 0 1 0 0]:
1 1 0 tan(a) 1 0 0 0 1
变换本质
前面我们总结canvas的时候,我们知道各种变换都是作用在用户坐标系上的。在SVG中,所有的变换也都是针对两个坐标系(本质上都是"用户坐标系")的。当给容器对象或图形对象指定"transform"属性,或者给"svg,symbol,marker,pattern,view"指定"viewBox"属性以后,SVG会根据当前的用户坐标系统进行变换,去创建新的用户坐标系,并作用于当前的对象以及它的子对象。该对象中指定的坐标和长度的单位不再是1:1的对应到外围的坐标系,而是随着变形,转换到新的用户坐标系中;这个新的用户坐标系是只作用于当前的元素及其子元素。
变换链
transform属性支持设置多个变换,这些变换只要中间用空格分开,然后一起放到属性中就可以了。执行效果跟按顺序独立执行这些变换是一样的。
上面的效果与下面的一样:
单位
最后说一下单位,任何坐标和长度都可以带和不带单位。
不带单位的情况
不带单位的值被认为带的是"用户单位",就是当前用户坐标系的单位值。
带单位的情况
svg中相关单位与CSS中是一样的:em,ex,px,pt,pc,cm,mm和in。长度还可以使用"%"。
相对度量单位:em和ex也与CSS中一样,是相对于当前字体的font-size和x-height来说的。
绝对度量单位:一个px是等于一个"用户单位"的,也就是"5px"与"5"是一样的。但是一个px是不是对应一个像素,那就看有没有进行过一些变换了。
其他的几个单位基本都是px的倍数:1pt=1.25px,1pc=15px,1mm=3.543307px,1cm=35.43307px,1in=90px。
如果最外层的SVG元素的width和height没有指定单位(也就是"用户单位"),则这些值会被认为单位是px。
SVG重用与引用
组合 - g元素
g元素是一种容器,它组合一组相关的图形元素成为一个整体;这样,我们就可以对这个整体进行操作。这个元素通常可以和desc和title元素配合使用,提供文档的结构信息。结构良好的文档通常可读性和渲染效率都不错。看一个小例子:
注意几点:1. xmlns="http://www.w3.org/2000/svg"表明了整个svg元素默认的命名空间是svg。这个在无歧义的时候可以省略。这里由于svg文档是一个XML文档,XML命名空间的相关规则这里都是适用的。例如可以给svg显示的指定命名空间,给命名空间提供别名等。
2. g元素是可以嵌套的。
3. 组合起来的图形元素就和单个的元素一样,可以给id值,这样,需要的时候(例如动画和重用一组元素)只用引用这个id值就可以了。
4. 组合一组图形元素可以统一设置这组元素的相关属性(fill,stroke,transform等),这也是使用组合的一种场景。
模板 - symbol元素
symbol元素用于定义图形模板(模板可以包含很多图形),这个模板可以被use元素实例化。模板的功能与g元素很相似,都是提供一组图形对象,但是也有一些区别。与g元素不同的地方是:
1.symbol元素本身是不会被渲染的,只有symbol模板的实例会被渲染。
2.symbol元素可以拥有属性viewBox和preserveAspectRatio,这些允许symbol缩放图形元素。
从渲染角度来说,与symbol元素相似的元素是marker(定义箭头和标号)和pattern(定义颜色)元素;这些元素不会直接被渲染;他们的使用方式基本都是由use元素去实例化。正是这个原因,对于symbol来说,'display'属性是没有意义的。
下面这个修改过的代码显示了symbol的使用方式:
定义 - defs元素
SVG允许定义一组对象,然后重用这组对象(注意,不仅仅是图形对象)。最常见的例子如定义渐变色,然后再其他的图形对象中赋给fill属性。渐变色定义的时候是不会渲染的,所以这类型的对象可以放到任何地方。重用对于图形对象中也是经常存在的,而且我们也不希望定义的时候直接渲染,而是想在引用的地方渲染,这个可以用defs元素实现。
通常情况下,推荐的做法是:只要有可能,就把被引用的对象放到defs元素中。这些对象通常是:altGlyphDef,clipPath,cursor,filter, marker,mask,pattern,linearGradient,radialGradient,symbol和图形对象等。把这些对象定义在defs元素中很容易理解,所以就提高了可访问性。
其实作为容器对象的g元素、symbol元素、defs元素都不同程度上提供了重用的作用,只不过每个元素的特性可能少许不同:比如g元素是直接渲染的,symbol和defs不会直接渲染,symbol含有viewBox属性,会创建新的视窗。
通常都会给在defs中定义的元素赋予id属性,并在用到的地方直接使用。根据元素的不同,这些定义可以用到不同地方,比如下面的渐进色作为属性来使用了:
图形相关元素的定义可以用use元素链接到文档。例如:
在这里请注意xlink名称空间的使用。尽管大多数查看器没有它也将正确显示这一项,但为了保持一致,xlink名称空间应该在<svg></svg>元素上定义。
引用 - use元素
任何svg, symbol, g, 单个的图形元素和use元素本质上都可以作为模板对象被use元素引用(例如初始化)。use引用的图形内容会在指定的位置渲染。与image元素不同,use元素不能引用整个文档。
use元素也有x, y, width和height属性,这些属性可以省略,如果不省略的话,会将被引用的图形内容坐标或长度映射到当前的用户坐标空间来。
use元素的作用过程就相当于把被引用的对象深拷贝一份到独立的非公开的DOM树中;这棵树的父节点是use元素。虽然是非公开的DOM节点,但是本质上还是DOM节点,所以被引用对象的所有属性值、动画、事件、CSS的相关设置等都会拷贝多来并都还是会起作用,而且这些节点也会继承use元素和use祖先的相关属性(注意引用元素是深拷贝,这些拷贝过来的元素与原来的元素已经无关系了,所以这里不会继承被引用元素祖先节点的属性),如果这些节点本身有相关(CSS)属性,还会覆盖继承来的属性,这些与普通的DOM节点是一致的,所以对use元素使用"visibility:hidden"时要小心,并不一定会起作用。但是由于这部分节点是非公开的,在DOM操作中,也只能看到use元素,所以也只能操作到use元素。
从视觉效果来看,use元素更像是占位符,渲染完成后的视觉效果就和直接用被引用对象渲染是一样的:
use元素引用一个symbol元素
(1) 把use元素换成g元素;
(2) 把use的除x,y,width,height,xlink:href外的属性全部移到g元素;
(3) 把use的x,y属性变成translate(x,y),追加到g元素的transform属性最后;
(4) 把引用的symbol元素换成svg元素,这个svg元素会显式使用use元素的width和height属性(use元素没有这些属性则是100%);
(5) 把引用的symbol元素的图形内容深拷贝到替换的svg中。
use元素引用一个svg元素
(1) 把use元素换成g元素;
(2) 把use的除x,y,width,height,xlink:href外的属性全部移到g元素;
(3) 把use的x,y属性变成translate(x,y),追加到g元素的transform属性最后;
(4) 把引用的svg元素包括内容拷贝过来,这个svg元素会显式使用use元素的width和height属性(use元素没有这些属性则使用原来的值);
其他情况
(1) 把use元素换成g元素;
(2) 把use的除x,y,width,height,xlink:href外的属性全部移到g元素;
(3) 把use的x,y属性变成translate(x,y),追加到g元素的transform属性最后;
(4) 把引用元素拷贝过来;
看下面例子的视觉效果:
下面的图和上面的图外观是一样的: