Jade Dungeon

SVG

基础

官方文档:http://www.w3.org/TR/SVG11/

嵌入HTML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html>
<head>
<title> My First SVG Page</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px">
	<rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black"/>
	<circle cx="100" cy="100" r="50" style="stroke: black; fill: red;"/>
</svg>
</body>
</html>

独立SVG文件

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px">
	<rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black"/>
	<circle cx="100" cy="100" r="50" style="stroke: black; fill: red;"/>
</svg>

exp-0102.svg

引用外部SVG文件:

<!DOCTYPE html>
<html>
<head>
<title> My First SVG Page</title>
</head>
<body>
<object data="sun.svg" type="image/svg+xml" width="300px" height="300px">
	<!-- Implement fallback code here, or display a message: -->
	<p>Your browser does not support SVG - please upgrade to a modern browser.</p>
</object>
<img src="exp-0102.svg" alt="svg not supported!" />
</body>
</html>

位图与文本的混合

<svg width="300" height="100" style="border: 1px solid yellow;">
  <image xlink:href="../wiki_images/svg/exp-a1a2.png" width="100" y="25"/>
  <text x="10" y="20" fill="white">An image inside SVG</text>
<svg>

exp-a1a1.png

<svg width="300" height="100" style="border: 1px solid yellow;">
  <image xlink:href="https://github.com/mastodon.png" width="100" y="25"/>
  <text x="10" y="20" fill="white">A cross-domain image inside SVG</text>
<svg>

SVG的渲染顺序

SVG是严格按照定义元素的顺序来渲染的,这个与HTML靠z-index值来控制分层不一样。 在SVG中,写在前面的元素先被渲染,写在后面的元素后被渲染。 后渲染的元素会覆盖前面的元素,虽然有时候受透明度影响,看起来不是被覆盖的, 但是SVG确实是严格按照先后顺序来渲染的。

注意:SVG是以XML定义的,所以是大小写敏感的,这点与HTML不一样。

图形绘制

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
		width="200" height="250">
	<rect x="10" y="10" width="30" height="30" stroke="black" fill="transparent" 
		stroke-width="5"/>
	<rect x="60" y="10" rx="10" ry="10" width="30" height="30" stroke="black" 
		fill="transparent" stroke-width="5"/>
	 
	<circle cx="25" cy="75" r="20" stroke="red" fill="transparent" 
		stroke-width="5"/>
	<ellipse cx="75" cy="75" rx="20" ry="5" stroke="red" fill="transparent" 
		stroke-width="5"/>
	 
	<line x1="10" x2="50" y1="110" y2="150" stroke="orange" fill="transparent" 
		stroke-width="5"/>
	<polyline points="60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145"
		stroke="orange" fill="transparent" stroke-width="5"/>
	 
	<polygon points="50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180" 
		stroke="green" fill="transparent" stroke-width="5"/>
	 
	<path d="M20,230 Q40,205 50,230 T90,230" fill="none" stroke="blue" 
		stroke-width="5"/>
</svg>

exp-0201.svg

基本元素

矩形 - rect元素

这个元素有6个控制位置和形状的属性,分别是:

  • x:矩形左上角的坐标(用户坐标系)的x值。
  • y:矩形左上角的坐标(用户坐标系)的y值。
  • width:矩形宽度。
  • height:矩形高度。
  • rx:实现圆角效果时,圆角沿x轴的半径。
  • ry:实现圆角效果时,圆角沿y轴的半径。

比如上面例子中,实现了圆角效果,你也可以通过设置rx,ry为不同的值实现椭圆角效果。

<rect x="10" y="10" width="30" height="30" stroke="black" fill="transparent" 
	stroke-width="5"/>

<rect x="60" y="10" rx="10" ry="10" width="30" height="30" stroke="black" 
	fill="transparent" stroke-width="5"/>

圆 - circle元素

这个元素的属性很简单,主要是定义圆心和半径:

  • r:圆的半径。
  • cx:圆心坐标x值。
  • cy:圆心坐标y值。
<ellipse cx="75" cy="75" rx="20" ry="5" stroke="red" fill="transparent" 
	stroke-width="5"/>

椭圆 - ellipse元素

这个是更加通用的圆形元素,你可以分别控制半长轴和半短轴的长度,来实现不同的椭圆, 很容易想到,当两个半轴相等时,就是正圆形了。

  • rx:半长轴(x半径)。
  • ry:半短轴(y半径)。
  • cx:圆心坐标x值。
  • cy:圆心坐标y值。
<ellipse cx="75" cy="75" rx="20" ry="5" stroke="red" fill="transparent" 
	stroke-width="5"/>

直线 - line元素

直线需要定义起点与终点即可:

  • x1:起点x坐标。
  • y1:起点y坐标。
  • x2:终点x坐标。
  • y2:终点y坐标。
<line x1="10" x2="50" y1="110" y2="150" stroke="orange" fill="transparent" 
	stroke-width="5"/>

折线 - polyline元素

折线主要是要定义每条线段的端点即可,所以只需要一个点的集合作为参数:

  • points:一系列的用空格,逗号,换行符等分隔开的点。每个点必须有2个数字: x值和y值。所以下面3个点(0,0), (1,1)和(2,2)可以写成:"0 0, 1 1, 2 2"。
<polyline 
	points="60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145"
	stroke="orange" fill="transparent" stroke-width="5"/>

多边形 - polygon元素

这个元素就是比polyline元素多做一步,把最后一个点和第一个点连起来,形成闭合图形。参数是一样的。

  • points:一系列的用空格,逗号,换行符等分隔开的点。每个点必须有2个数字: x值和y值。所以下面3个点 (0,0), (1,1)和(2,2)可以写成:"0 0, 1 1, 2 2"。
<polygon 
	points="50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180" 
	stroke="green" fill="transparent" stroke-width="5"/>

路径 - path元素

这个是最通用,最强力的元素了;使用这个元素你可以实现任何其他的图形, 不仅包括上面这些基本形状,也可以实现像贝塞尔曲线那样的复杂形状; 此外,使用path可以实现平滑的过渡线段,虽然也可以使用polyline来实现这种效果, 但是需要提供的点很多,而且放大了效果也不好。 这个元素控制位置和形状的只有一个参数:

  • d:一系列绘制指令和绘制参数(点)组合成。
<path d="M20,230 Q40,205 50,230 T90,230" fill="none" stroke="blue" 
	stroke-width="5"/>

绝对坐标与相对坐标

绘制指令分为绝对坐标指令和相对坐标指令两种,这两种指令使用的字母是一样的, 就是大小写不一样,绝对指令使用大写字母,坐标也是绝对坐标; 相对指令使用对应的小写字母,点的坐标表示的都是偏移量。

绝对坐标绘制指令

这组指令的参数代表的是绝对坐标。假设当前画笔所在的位置为(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都比较简单。

相对坐标绘制指令

与绝对坐标绘制指令的字母是一样的,只不过全部是小写表示。 这组指令的参数中涉及坐标的参数代表的是相对坐标, 意思就是参数代表的是从当前点到目标点的偏移量,正数就代表向轴正向偏移, 负数代表向反向偏移。不过对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代表旋转的角度,体会下面例子中圆弧的不同:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
		width="320px" height="320px">
	<path d="M10 315
		L 110 215
		A 30 50 0 0 1 162.55 162.45
		L 172.55 152.45
		A 30 50 -45 0 1 215.1 109.9
		L 315 10" stroke="black" fill="green" stroke-width="2" fill-opacity="0.5"/>
</svg>

原理:

exp-0202.png

实现效果:

exp-0202.svg

从图中可以看到椭圆旋转参数的不同导致绘制的圆弧方向不同, 当然这个参数对正圆来说没有影响。

圆弧大小和走向

large-arc-flagsweep-flag控制了圆弧的大小和走向,体会下面例子中圆弧的不同:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
		width="325px" height="325px">

	<path d="M80 80
		A 45 45, 0, 0, 0, 125 125
		L 125 80 Z" fill="green"/>

	<path d="M230 80
		A 45 45, 0, 1, 0, 275 125
		L 275 80 Z" fill="red"/>

	<path d="M80 230
		A 45 45, 0, 0, 1, 125 275
		L 125 230 Z" fill="purple"/>

	<path d="M230 230
		A 45 45, 0, 1, 1, 275 275
		L 275 230 Z" fill="blue"/>
</svg>

原理:

exp-0203.png

实现效果:

exp-0203.svg

从上面可以看出,这几个参数其实是唯一确定一段圆弧需要的参数。 这里也看到,SVG中的圆弧比canvas中的复杂一些。

绘制三次贝塞尔曲线

C  x1 y1, x2 y2, x y

三次贝塞尔曲线有两个控制点,就是(x1,y1)和(x2,y2),最后面(x,y)代表曲线的终点。体会下面的例子:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
	width="190px" height="160px">
	<path d="M10   10 C  20  20,  40  20,  50  10" stroke="black" fill="transparent"/>
	<path d="M70   10 C  70  20, 120  20, 120  10" stroke="black" fill="transparent"/>
	<path d="M130  10 C 120  20, 180  20, 170  10" stroke="black" fill="transparent"/>

	<path d="M10   60 C  20  80,  40  80,  50  60" stroke="black" fill="transparent"/>
	<path d="M70   60 C  70  80, 110  80, 110  60" stroke="black" fill="transparent"/>
	<path d="M130  60 C 120  80, 180  80, 170  60" stroke="black" fill="transparent"/>

	<path d="M10  110 C  20 140,  40 140,  50 110" stroke="black" fill="transparent"/>
	<path d="M70  110 C  70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
	<path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>
</svg>

原理:

exp-0204.png

实现效果:

exp-0204.svg

从上面我们可以看到,控制点控制了曲线的弧度。

三次贝塞尔曲线

特殊版本的三次贝塞尔曲线:

S  x2 y2, x y

很多时候,为了绘制平滑的曲线,需要多次连续绘制曲线。这个时候,为了平滑过渡, 常常第二个曲线的控制点是第一个曲线控制点在曲线另外一边的映射点。 这个时候可以使用这个简化版本。这里要注意的是:

  • 如果S指令前面没有其他的S指令或C指令,这个时候会认为两个控制点是一样的, 退化成二次贝塞尔曲线的样子;
  • 如果S指令是用在另外一个S指令或者C指令后面, 这个时候后面这个S指令的第一个控制点会默认设置为前面的这个曲线的第二个控制点的一个映射点。
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
	svg width="190px" height="160px">
	<path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" 
		stroke="black" fill="transparent"/>
</svg>

原理:

exp-0205.png

实现效果:

exp-0205.svg

特殊版本的二次贝塞尔曲线

二次贝塞尔曲线只有一个控制点(x1,y1),通常如下图所示:

Q  x1 y1, x y

例子:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
	width="190px" height="160px">
  <path d="M10 80 Q 90 10, 180 80" stroke="black" fill="transparent"/>
</svg>

原理:

exp-0206.png

实现效果:

exp-0206.svg

如果是连续的绘制曲线,同样可以使用简化版本T。同样的,只有T前面是Q或者T指令的时候, 后面的T指令的控制点会默认设置为前面的曲线的控制点的映射点:

Q  x1 y1, x y , T x y

例子:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
	width="190px" height="160px">
  <path d="M10 80 Q 52.5 10, 95 80 T 180 80" 
		stroke="black" fill="transparent"/>
</svg>

原理:

exp-0207.png

实现效果:

exp-0207.svg

SVG path绘制注意事项:

绘制带孔的图形时要注意: 外层边的绘制需要是逆时针顺序的,里面的洞的边的顺序必须是顺时针的。 只有这样绘制的图形填充效果才会正确。

文本与图像

SVG的强大能力之一是它可以将文本控制到标准HTML页面不可能有的程度, 而无须求助图像或其它插件。任何可以在形状或路径上执行的操作(如绘制或滤镜) 都可以在文本上执行。尽管SVG的文本渲染如此强大,但是还是有一个不足之处: SVG不能执行自动换行。如果文本比允许空间长,则简单地将它切断。多数情况下, 创建多行文本需要多个文本元素。

此外,可以使用tspan元素可以将文本元素分成几部分,允许每部分有各自的样式。

还有,在text元素中,空格的处理与HTML类似:换行和回车变成空格,而多个空格压缩成单个空格。

text元素直接显示文本

直接显示文本,可以使用text元素,例子如下:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" >  
  <rect width="300" height="200" fill="red" />  
  <circle cx="150" cy="100" r="80" fill="green" />  
  <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>  
</svg> 

如上面的例子中所示,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元素的子元素中。典型的用法就是强调显示部分文本。例如:

<text>
  <tspan font-weight="bold" fill="red">This is bold and red</tspan>
</text>

x,y用于设置包含的文本的绝对坐标值,这个值会覆盖默认的文本位置。 这些属性可以包含一系列数字,这些数字会应用到每个对应的单个字符。 没有对应设置的字符会紧跟前一个字符。例如:

  • dx,dy用于设置包含的文本相对于默认的文本位置的偏移量。这些属性同样可以包含一系列数字,每个都会应用到对应的字符。没有对应设置的字符会紧跟前一个字符。你可以把上面的例子中的x换成dx看看效果。
  • rotate用于设置字体的旋转角度。这个属性页可以包含一系列数字,应用到每个字符。没有对应设置的字符会使用最后设置的那个数字。
  • textLength:这是最令人费解的属性,据说设置完以后,渲染发现文本的长度与这个值不一致时,会以这个长度为准。但是我没有试出来效果。

x,y参数例子:

<text x="10" y="10">Hello World!
  <tspan x="100 200 300"  font-weight="bold" fill="red">This is bold and red</tspan>
</text>

dx,dy参数例子:

<text x="10" y="10">Hello World!
 <tspan rotate="10 20 45"  font-weight="bold" fill="red">This is bold and red</tspan>
</text>

文本引用 - tref元素

这个元素允许引用定义过的文本,并高效的拷贝到当前位置,通常配合xlink:href指定目的元素。 因为是拷贝过来的,所以使用css修改当前文本的时候,不会修改原来的文本。看例子:

<text id="example">This is an example text.</text>
<text>
    <tref xlink:href="#example" />
</text>

文本路径 - textPath元素

这个比较有意思,效果也很酷,能做出很多的艺术效果; 这个元素从它的xlink:href属性获取指定的路径并把文本对齐到这个路径上,看例子:

<path id="my_path" d="M 20,20 C 40,40 80,40 100,20" />
<text>
  <textPath xlink:href="#my_path">This text follows a curve.</textPath>
</text>

SVG中渲染图片 - image元素

SVG中的image元素可以直接支持显示光栅图片,使用很简单。看下面的例子:

<svg width="5cm" height="4cm">
  <image xlink:href="Penguins.jpg" x="0" y="0" height="50px" width="50px"/>
</svg>

这里需要注意几点:

  1. 如果没有设置x或y坐标,则默认是0。
  2. 如果没有设置width或height,则默认也是0.
  3. 如果显式的设置width或height为0,则会禁止渲染这幅图片。
  4. 图片的格式支持png,jpeg,jpg,svg等等,所以svg是支持嵌套svg的。
  5. image与其他元素一样,是svg的常规元素,所以它支持所有的裁剪,蒙板,滤镜,旋转等效果。

笔画与填充

前面我们重点都在总结各类形状,文本和图片,接下来,我们还是和讨论canvas一样, 总结一下颜色处理,也就是填充和边框效果;你会发现这里的内容与canvas基本上是一致的。 这些属性既可以以属性的形式写在元素中,也可以以CSS的形式保存(这是与canvas不一样的地方)。

填充色 - fill属性

这个属性使用设置的颜色填充图形内部,使用很简单,直接把颜色值赋给这个属性就可以了。看例子:

<rect x="10" y="10" width="100" height="100" stroke="blue" fill="red"
	fill-opacity="0.5" stroke-opacity="0.8"/>

exp-0301.svg

上面例子中画了一个红色蓝边的矩形。注意几点:

  • 如果不提供fill属性,则默认会使用黑色填充,如果要取消填充,需要设置成none。
  • 可以设置填充的透明度,就是fill-opacity,值的范围是0到1。
  • 稍微复杂一点的是fill-rule属性。这个属性定义了判断点是不是属于填充范围的算法; 除了inherit这个值外,还有两个取值nonzeroevenodd

nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:

exp-0302.png

evenodd:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则改点在图形内,需要填充;个数为偶数则点在图形外,不需要填充。看下图的示例:

exp-0303.png

边框色 - stroke属性

上面的例子中已经用到了stroke属性,这个属性使用设置的值画图形的边框,使用起来也很直接,把颜色值赋给它就可以了。注意:

  1. 如果不提供stroke属性,则默认不绘制图形边框。
  2. 可以设置边的透明度,就是stroke-opacity,值的范围是0到1。

实际上,边的情况比图形内部稍微复杂一点,因为边除了颜色,还有"形状"需要定义。

线的端点 - stroke-linecap属性

这个属性定义了线段端点的风格,这个属性可以使用butt,square,round三个值。看例子:

<svg width="160" height="140">
  <line y2="20"  stroke-linecap="butt"   stroke="black" stroke-width="20" x1="40" x2="120" y1="20" />
  <line y2="60"  stroke-linecap="square" stroke="black" stroke-width="20" x1="40" x2="120" y1="60" />
  <line y2="100" stroke-linecap="round"  stroke="black" stroke-width="20" x1="40" x2="120" y1="100"/>
</svg>

exp-0304.png

这段代码绘制了3条使用不同风格线端点的线,从的图中我们可以很容易看出3中风格的不同。

线的连接 - stroke-linejoin属性

这个属性定义了线段连接处的风格,这个属性可以使用miter,round,bevel三个值。看例子:

<svg width="160" height="280">
  <polyline points="40 60 80 20 120 60" stroke="black" stroke-width="20"
      stroke-linecap="butt" fill="transparent" stroke-linejoin="miter"/>
  
  <polyline points="40 140 80 100 120 140" stroke="black" stroke-width="20"
      stroke-linecap="round" fill="transparent" stroke-linejoin="round"/>
  
  <polyline points="40 220 80 180 120 220" stroke="black" stroke-width="20"
      stroke-linecap="square" fill="transparent" stroke-linejoin="bevel"/>
</svg>

exp-0305.png

线的虚实 - stroke-dasharray属性

这个属性可以设置线段采用何种虚实线。看例子:

<svg width="200" height="150">
  <path d="M 10 75 Q 50 10 100 75 T 190 75" stroke="black"
    stroke-linecap="round" stroke-dasharray="5,10,5" fill="none"/>
  <path d="M 10 75 L 190 75" stroke="red"
    stroke-linecap="round" stroke-width="1" stroke-dasharray="5,5" fill="none"/>
</svg>

这个属性是设置一些列数字,不过这些数字必须是逗号隔开的。

属性中当然可以包含空格,但是空格不作为分隔符。每个数字定义了实线段的长度, 分别是按照绘制、不绘制这个顺序循环下去。

所以左面的例子中绘制的线是画5单位的实线,留5单位的空格, 再画5单位的实线...这样一直下去。

exp-0306.png

除了这些常用的属性,还有下列属性可以设置:

  • stroke-miterlimit:这个和canvas中的一样,它处理什么时候画和不画线连接处的miter效果。
  • stroke-dashoffset:这个属性设置开始画虚线的位置。

使用CSS展示数据

HTML5强化了DIV+CSS的思想,所以展示数据的部分还可以交给CSS处理。 与普通HTML元素相比,只不过是background-colorborder换成了fillstroke。 其他的大多都差不多。简单看个例子:

#MyRect:hover { stroke: black; fill: blue; }

颜色的表示

SVG和canvas中是一样的,都是使用标准的HTML/CSS中的颜色表示方法, 这些颜色都可以用于fillstroke属性。

基本有下面这些定义颜色的方式:

  1. 颜色名字: 直接使用颜色名字red, blue, black...
  2. rgba/rgb值: 这个也很好理解,例如#ff0000,rgba(255,100,100,0.5)
  3. 十六进制值: 用十六进制定义的颜色,例如#ffffff

颜色的渐变

渐变值:这个也与canvas中一样,支持两种渐变色:线性渐变,环形渐变。如下图所示:

exp-0401.png

线性渐变

使用linearGradient元素即可定义线性渐变,每一个渐变色成分使用stop元素定义。 看下面的例子:

<svg width="120" height="240">  
	<defs>  
		<linearGradient id="Gradient1">  
			<stop class="stop1" offset="0%"/>  
			<stop class="stop2" offset="50%"/>  
			<stop class="stop3" offset="100%"/>  
		</linearGradient>  
		<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">  
			<stop offset="0%" stop-color="red"/>  
			<stop offset="50%" stop-color="black" stop-opacity="0"/>  
			<stop offset="100%" stop-color="blue"/>  
		</linearGradient>  
		<style type="text/css"><![CDATA[  
			#rect1 { fill: url(#Gradient1); }  
			.stop1 { stop-color: red; }  
			.stop2 { stop-color: black; stop-opacity: 0; }  
			.stop3 { stop-color: blue; }]]>
		</style>  
	</defs>  
	   
	<rect id="rect1" x="10" y="10" rx="15" ry="15" width="100" height="100"/>  
	<rect x="10" y="120" rx="15" ry="15" width="100" height="100" fill="url(#Gradient2)"/>     
</svg>  

在这个例子中,我们需要注意:

  1. 渐变色元素必须要放到defs元素中;
  2. 需要给渐变色元素设置id值,否则的话,别的元素无法使用这个渐变色。
  3. 渐变色的成员使用stop定义,它的属性也可以使用CSS定义;它支持class,id这种标准HTML都支持的属性。其它常用属性如下:
    • offset属性:这个定义了该成员色的作用范围,该属性取值从0%到100%(或者是0到1);通常第一种颜色都是设置成0%,最后一种设置成100%。
    • stop-color属性:这个很简单,定义了该成员色的颜色。
    • stop-opacity属性:定义了成员色的透明度。
    • x1,y1,x2,y2属性:这两个点定义了渐变的方向,默认不写的话是水平渐变,上面例子中同时也创建了一个垂直渐变。
  4. 渐变色的使用,如例子中所示,直接用url(#id)的形式赋值给fill或者stroke就可以了。
  5. 渐变色成员的复用:你也可以使用xlink:href引用定义过的渐变色成员,所以上面的例子也可以改写如下:
<linearGradient id="Gradient1">  
	<stop class="stop1" offset="0%"/>  
	<stop class="stop2" offset="50%"/>  
	<stop class="stop3" offset="100%"/>  
</linearGradient> 
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1" xlink:href="#Gradient1"/>

环形渐变

使用radialGradient元素定义环形渐变,还是使用stop定义成员色。看例子:

<svg width="120" height="240">
	<defs>
		<radialGradient id="Gradient3">
			<stop offset="0%" stop-color="red"/>
			<stop offset="100%" stop-color="blue"/>
		</radialGradient>
		<radialGradient id="Gradient4" cx="0.25" cy="0.25" r="0.25">
			<stop offset="0%" stop-color="red"/>
			<stop offset="100%" stop-color="blue"/>
		</radialGradient>
	</defs>
	
	<rect x="10" y="10" rx="15" ry="15" width="100" height="100" fill="url(#Gradient3)"/> 
	<rect x="10" y="120" rx="15" ry="15" width="100" height="100" fill="url(#Gradient4)"/> 
</svg>

从上面的例子看到,除了元素名字和一些特别的成员,其他的所有都和线性渐变一样,包括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:

<svg width="120" height="120">
	<defs>
		<radialGradient id="Gradient5" cx="0.5" cy="0.5" r="0.5" fx="0.25" fy="0.25">
			<stop offset="0%" stop-color="red"/>
			<stop offset="100%" stop-color="blue"/>
		</radialGradient>
	</defs>
	
	<rect x="10" y="10" rx="15" ry="15" width="100" height="100"
		fill="url(#Gradient5)" stroke="black" stroke-width="2"/>
	
	<circle cx="60" cy="60" r="50" fill="transparent" stroke="white" stroke-width="2"/>
	<circle cx="35" cy="35" r="2" fill="white" stroke="white"/>
	<circle cx="60" cy="60" r="2" fill="white" stroke="white"/>
	<text x="38" y="40" fill="white" font-family="sans-serif" font-size="10pt">(fx,fy)</text>
	<text x="63" y="63" fill="white" font-family="sans-serif" font-size="10pt">(cx,cy)</text> 
</svg>

看效果图就知道"焦点"的含义了。

exp-0402.png

此外,还有渐变色元素还有一些变换的属性,如gradientTransform,这个不是这里的重点,后面会总结变换。

另外一个可能用到的属性是spreadMethod属性,这个属性定义了渐变色到达它的终点时应该采取的行为。 该属性有3个可选值:pad(默认值),reflect,repeat。

  • pad不用说了,属于自然过渡,渐变色结束以后,使用最后一个成员色直接渲染对象剩下的部分。
  • refect会让渐变色继续,只不过渐变色会反向继续渲染,从最后一个颜色开始到第一个颜色这个顺序渲染;等到再次到达渐变色终点时,再反序,如此这般指导对象填充完毕。
  • repeat也会让渐变色继续渲染,但是不会反序,还是一遍一遍从第一种颜色到最后一种颜色渲染。效果图如下所示:

exp-0403.png

<svg width="220" height="220">
	<defs>
		<radialGradient id="Gradient"
				cx="0.5" cy="0.5" r="0.25" fx=".25" fy=".25"
				spreadMethod="repeat">
			<stop offset="0%" stop-color="red"/>
			<stop offset="100%" stop-color="blue"/>
		</radialGradient>
	</defs>
	<rect x="50" y="50" rx="15" ry="15" width="100" height="100"
		fill="url(#Gradient)"/>
</svg>

纹理填充

纹理填充也是一种流行的填充方式,在SVG中,可以使用pattern创建一个纹理,然后用这个pattern去填充别的对象。直接看例子:

<svg width="200" height="200">
	<defs>
		<linearGradient id="Gradient6">
			<stop offset="0%" stop-color="white"/>
			<stop offset="100%" stop-color="blue"/>
		</linearGradient>
		<linearGradient id="Gradient7" x1="0" x2="0" y1="0" y2="1">
			<stop offset="0%" stop-color="red"/>
			<stop offset="100%" stop-color="orange"/>
		</linearGradient>
	</defs>
	<defs>
		<pattern id="Pattern" x=".05" y=".05" width=".25" height=".25">
			<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
			<rect x="0" y="0" width="25" height="25" fill="url(#Gradient7)"/>
			<circle cx="25" cy="25" r="20" fill="url(#Gradient6)" fill-opacity="0.5"/>
		</pattern> 
	</defs>
	
	<rect fill="url(#Pattern)" stroke="black" x="0" y="0" width="200" height="200"/>
</svg>

exp-0404.png

例子看起来很简单,由渐变色创建pattern,然后使用pattern填充矩形。这里需要注意:

  1. 不同的浏览器填充这个pattern的时候效果不一样。比如例子在FireFix和Chrome中效果一样。但是如果你把渐变色和pattern定义在同一个defs组合里,则FireFox仍然能正常渲染,但是Chrome就识别不了渐变色,只会用默认的黑色填充。
  2. pattern也需要定义id。
  3. pattern也必须要定义在defs中。
  4. pattern的使用也是把url(#id)直接赋值给fill或stroke。

上面这些都是很简单的,我们重点看一下例子中的坐标表示情况,坐标在pattern中比较复杂。

pattern中包含两个相关属性:patternUnits和patternContentUnits属性; 这两个属性的取值都还是只有2个:objectBoundingBox和userSpaceOnUse,这两个值的含义上面以及讲过了。 这里容易混淆的是这两个属性的默认值不同,但是当你理解这么做的原因以后,你又会发现这么做还真是有道理。

1. patternUnits属性

这个属性与Gradient的gradientUnits属性是一样的,默认采用objectBoundingBox。受这个属性影响的属性有x,y,width,height,这4个属性分别定义了pattern的起点,宽高度。它们都采用了相对值,例子中想要在水平和竖直方向上都填充4次,所以width和height都设为了0.25。

2. 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。 那么pattern为什么要这么设置两个属性的默认值呢?

这是由用户的使用决定的(以上面的例子来讨论):

第一种pattern样式:我想这是大多数情况,所以处理成默认值:pattern是会随着外面的图形缩放而被拉伸,不管外围方形是多大,pattern始终在两个方向上都会被填充4次。但是pattern中包含的图形是不会随着外面被填充的方形缩放而进行拉伸的。虽然比较牵强,但就这么理解吧。

第二种pattern样式:pattern中的形状也随着外围的形状缩放进行拉伸。我们可以显示的把patternContentUnits属性的值也设为objectBoundingBox达到这个效果。例如把pattern的部分修改如下:

<pattern id="Pattern" width=".25" height=".25" patternContentUnits="objectBoundingBox">
   <rect x="0" y="0" width=".25" height=".25" fill="skyblue"/>
   <rect x="0" y="0" width=".125" height=".125" fill="url(#Gradient2)"/>
   <circle cx=".125" cy=".125" r=".1" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>

修改后,当改变被填充的矩形的大小时,pattern中的形状也会进行拉伸。而且修改后改成了相对外围对象的坐标,所以不再需要pattern的x和y坐标了,pattern会始终调整以适合被填充的形状。

第三种pattern的样式:pattern的形状和大小都是固定了,不管外围对象怎么缩放,你可以把坐标系统都改成userSpaceOnUse实现这个效果。代码如下:

<pattern id="Pattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
   <rect x="0" y="0" width="50" height="50" fill="skyblue"/>
   <rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
   <circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>

这3中典型的pattern如下图所示:

exp-0405.png

坐标与变换

坐标系统

SVG存在两套坐标系统:视窗坐标系与用户坐标系。默认情况下,用户坐标系与视窗坐标系的点是一一对应的,都为原点在视窗的左上角,x轴水平向右,y轴竖直向下;如下图所示:

exp-0601.png

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)。

体会下面几种代码绘出的结果的不同:

svg width="200" height="200" viewBox="0 0 200 200">
 <rect x="0" y="0" width="200" height="200" fill="Red" />
 <rect x="0" y="0" width="100" height="100" fill="Green" />
</svg>

上面的例子绘制的图中你可以看到绿色和红色的矩形,这种情况下视窗坐标系的点还是与视窗上的点是一一对应的,这个也是默认情况。

<svg width="200" height="200" viewBox="0 0 100 100">
 <rect x="0" y="0" width="200" height="200" fill="Red" />
 <rect x="0" y="0" width="100" height="100" fill="Green" />
</svg>

上面的例子绘制的图中这个你只能看到绿色的矩形,而且绿色的矩形显示在屏幕上是200*200像素的,这个时候坐标点已经不是一一对应了,图被放大了。

<svg width="200" height="200" viewBox="0 0 400 400">
 <rect x="0" y="0" width="200" height="200" fill="Red" />
 <rect x="0" y="0" width="100" height="100" fill="Green" />
</svg>

上面的例子绘制的图中,视窗坐标系的单位被缩小,所以两个矩形都缩小了。

在日常工作中,我们经常需要完成的一个任务就是缩放一组图形,让它适应它的父容器。我们可以通过设置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,超出的部分被剪裁掉。

下图诠释了各种填充的效果:

exp-0602.png


用户坐标系的变换 - transform属性

该类型变换是通过设置元素的transform属性来指定的。这里需要注意,transform属性设置的元素的变换,只影响该元素及其子元素,与别的元素无关,不影响别的元素。

平移 - translate

平移变换把相关的坐标值平移到指定的位置,该变换需要传入两个轴上平移的量。看例子:

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

这个例子绘制一个矩形,并把它的起点(0,0)平移到(30,40)处。虽然可以直接设置(x,y)的坐标值,但是使用平移变换去实现也很方便。这个变换第二个参数可以省略,默认当0处理。

旋转 - rotate

旋转一个元素也是一个很常见的任务,我们可以使用rotate变换实现,该变换需要传入旋转的角度参数。看例子:

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

这个例子会显示一个旋转45度的矩形。有几点注意:

  1. 这里的变换是以角度值为参数的。
  2. 旋转指的是相对于x轴的旋转。
  3. 旋转是围绕用户坐标系的原点(0,0)展开的。
倾斜 - skew

transform还支持倾斜变换,可以是沿着x轴的(左右倾斜,正角度为向右倾斜,其实是倾斜了y轴),或者是沿着y轴的(上下倾斜,正角度为向下倾斜,其实是倾斜了x轴)倾斜;该变换需要传入一个角度参数,这个角度参数会决定倾斜的角度。看下面的例子:

<svg width="100" height="100">
  <rect x="0" y="0" width="100" height="100" fill="green" />
  <circle cx="15" cy="15" r="15" fill="red" />
  <circle cx="15" cy="15" r="15" fill="yellow" transform="skewX(45)" />
  <rect x="30" y="30" width="20" height="20"  />
  <rect x="30" y="30" width="20" height="20" transform="skewX(45)"  />
  <rect x="30" y="30" width="20" height="20" transform="skewY(45)"  />
</svg>

从结果中,你可以直接看到同样尺寸的矩形,在不同的倾斜变换后,得到的位置和形状。这里注意矩形的起始位置都已经改变了,这是因为在新的坐标系统中,(30,30)已经在不同的位置了。

缩放 - scale

缩放对象由缩放变换完成,该变换接受2个参数,分别指定在水平和竖直上的缩放比例,如果第二个参数省略则与第一个参数取相同的值。看下面的例子:

<svg width="500" height="500">
 <text x="20" y="20" font-size="20">ABC (scale)</text>
 <text x="50" y="50" font-size="20" transform="scale(1.5)">ABC (scale)</text>
</svg>
变换矩阵 - 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属性支持设置多个变换,这些变换只要中间用空格分开,然后一起放到属性中就可以了。执行效果跟按顺序独立执行这些变换是一样的。

<g transform="translate(-10,-20) scale(2) rotate(45) translate(5,10)">
  <!-- graphics elements go here -->
</g>

上面的效果与下面的一样:

<g transform="translate(-10,-20)">
  <g transform="scale(2)">
    <g transform="rotate(45)">
      <g transform="translate(5,10)">
        <!-- graphics elements go here -->
      </g>
    </g>
  </g>
</g>

单位

最后说一下单位,任何坐标和长度都可以带和不带单位。

不带单位的情况

不带单位的值被认为带的是"用户单位",就是当前用户坐标系的单位值。

带单位的情况

  • 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元素配合使用,提供文档的结构信息。结构良好的文档通常可读性和渲染效率都不错。看一个小例子:

<svg xmlns="http://www.w3.org/2000/svg"
     version="1.1" width="5cm" height="5cm">
  <desc>Two groups, each of two rectangles</desc>
  <g id="group1" fill="red">
    <rect x="1cm" y="1cm" width="1cm" height="1cm"/>
    <rect x="3cm" y="1cm" width="1cm" height="1cm"/>
  </g>
  <g id="group2" fill="blue">
    <rect x="1cm" y="3cm" width="1cm" height="1cm"/>
    <rect x="3cm" y="3cm" width="1cm" height="1cm"/>
  </g>

  <!-- Show outline of canvas using 'rect' element -->
  <rect x=".01cm" y=".01cm" width="4.98cm" height="4.98cm"
        fill="none" stroke="blue" stroke-width=".02cm"/>
</svg>

注意几点:

  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的使用方式:

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.1" width="5cm" height="5cm">
  <desc>Two groups, each of two rectangles</desc>
  <symbol id="group1" fill="red">
    <rect x="1cm" y="1cm" width="1cm" height="1cm"/>
    <rect x="3cm" y="1cm" width="1cm" height="1cm"/>
  </symbol>
  <g id="group2" fill="blue">
    <rect x="1cm" y="3cm" width="1cm" height="1cm"/>
    <rect x="3cm" y="3cm" width="1cm" height="1cm"/>
  </g>
  <use xlink:href="#group1" target="_blank" rel="nofollow">

  <!-- Show outline of canvas using 'rect' element -->
  <rect x=".02cm" y=".02cm" width="4.96cm" height="4.96cm"
        fill="none" stroke="blue" stroke-width=".02cm"/>
</svg>

定义 - 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属性,并在用到的地方直接使用。根据元素的不同,这些定义可以用到不同地方,比如下面的渐进色作为属性来使用了:

<svg width="8cm" height="3cm"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Local URI references within ancestor's 'defs' element.</desc>
  <defs>
    <linearGradient id="Gradient01">
      <stop offset="20%" stop-color="#39F" />
      <stop offset="90%" stop-color="#F3F" />
    </linearGradient>
  </defs>
  <rect x="1cm" y="1cm" width="6cm" height="1cm" 
        fill="url(#Gradient01)"  />
</svg>

图形相关元素的定义可以用use元素链接到文档。例如:

<svg width="10cm" height="3cm" viewBox="0 0 100 30" version="1.1"
     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <desc>Example Use01 - Simple case of 'use' on a 'rect'</desc>
  <defs>
    <rect id="MyRect" width="60" height="10"/>
  </defs>
  <rect x=".1" y=".1" width="99.8" height="29.8"
        fill="none" stroke="blue" stroke-width=".2" />
  <use x="20" y="10" xlink:href="#MyRect" />
</svg>

在这里请注意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. 把引用元素拷贝过来;

看下面例子的视觉效果:

<svg width="10cm" height="3cm" viewBox="0 0 100 30" version="1.1"
     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <desc>Example Use03 - 'use' with a 'transform' attribute</desc>
  <defs>
    <rect id="MyRect" x="0" y="0" width="60" height="10"/>
  </defs>
  <rect x=".1" y=".1" width="99.8" height="29.8"
        fill="none" stroke="blue" stroke-width=".2" />
  <use xlink:href="#MyRect"
       transform="translate(20,2.5) rotate(10)" />
</svg>

下面的图和上面的图外观是一样的:

<svg width="10cm" height="3cm" viewBox="0 0 100 30"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example Use03-'use' with a 'transform' attribute</desc>

  <rect x=".1" y=".1" width="99.8" height="29.8"
        fill="none" stroke="blue" stroke-width=".2" />

  <g transform="translate(20,2.5) rotate(10)">
    <rect x="0" y="0" width="60" height="10"/>
  </g>
</svg>

文档结构

前面介绍了很多的基本元素,包括结构相关的组合和重用元素,这里先对SVG的文档结构中剩下的相关元素简单总结一下,然后继续向前领略SVG的其他特性。

SVG文档的元素基本可以分为以下几类:

  • 动画元素:animate, animateColor, animateMotion, animateTransform, set;
  • 解释元素:desc, metadata, title;
  • 图形元素:circle, ellipse, line, path, polygon, polyline, rect;
  • 结构元素:defs, g, svg, symbol, use;
  • 渐变元素:linearGradient, radialGradient;
  • 其他元素:a,altGlyphDef,clipPath,color-profile,cursor,filter,font,font-face,foreignObject,image,marker,mask,pattern,script,style,switch,text,view等。

其中图形元素,渐变元素,文本,图像元素和组合等都介绍过了,下面介绍另外几个与结构相关的元素。

视窗 - svg元素

可以在svg元素中以任何顺序放置任何的其他元素,包括嵌套svg元素。

svg元素支持的属性常用的也就是id,class,x,y,width,height,viewBox,preserveAspectRatio,以及fill和stroke的相关属性。

svg元素支持的事件也是常用的onload, onmouseover, onmousemove, onmousedown, onmouseup, onclick, onfocusin, onfocusout, onresize, onscroll, onunload等。svg元素就不多说了,完整的属性和事件列表参看后面的官方文档。

解释性元素 - desc元素与title元素

每个容器元素(可以包含其他容器元素或者图形元素的元素,例如:a,defs,glyph,g,marker,mask,missing-glyph,pattern,svg,switch和symbol)和图形元素都可以包含desc和title元素,这两个元素都是辅助性的元素,用于解释相关情境;它们的内容都是文本。当SVG文档被渲染的时候,这2个元素不会被渲染到图形中。这个2个元素之间差别不是太大,title在有些实现中是作为提示信息出现的,所以通常title是放到父元素的第一个位置上。

典型的用法如下:

<svg xmlns="http://www.w3.org/2000/svg"
     version="1.1" width="4in" height="3in">
  <g>
    <title>Company sales by region</title>
    <desc>
      This is a bar chart which shows 
      company sales by region.
    </desc>
    <!-- Bar chart defined as vector data -->
  </g>
</svg>

通常,最外层的svg元素要配以title说明,这样程序可读性更好。

标记 - marker元素

标记定义了附加到一个或者多个顶点(path,line,polyline或者polygon的顶点)上的图形元素(箭头和多点标记)。箭头可以通过把一个标记附加到path,line或者polyline的起点或者终点上。多点标记可以把一个标记附加到path,line,polyline或者polygon的所有顶点上。

标记是由marker元素定义的,然后在path,line,polyline或者polygon中设置相关的属性(marker,marker-start,marker-mid,和marker-end)就可以了。看个例子:

<svg width="4in" height="2in" viewBox="0 0 4000 2000" version="1.1"
     xmlns="http://www.w3.org/2000/svg">
  <defs>
    <marker id="Triangle"
      viewBox="0 0 10 10" refX="0" refY="5" 
      markerUnits="strokeWidth"
      markerWidth="4" markerHeight="3"
      orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" />
    </marker>
  </defs>
  <desc>Placing an arrowhead at the end of a path.
  </desc>
  <path d="M 1000 750 L 2000 750 L 2500 1250"
        fill="none" stroke="black" stroke-width="100" 
        marker-end="url(#Triangle)"  />
</svg>

下面详细看看marker的相关知识:

marker是容器元素,可以存放任意顺序的图形元素,容器元素,动画,渐变元素等。

marker元素可以创建新的视窗:设置viewBox的值。

marker比较重要的属性:

  • markerUnits = "strokeWidth | userSpaceOnUse" 这个属性定义了属性markerWidth,markerHeight和marker的内容使用的坐标系统。这个属性有2个值可选,第一个值strokeWidth是默认值,代表属性markerWidth,markerHeight和marker的内容使用的坐标系统的单位等于引用该marker的图形元素的stroke-width设置的值。 例如上面的例子中,marker元素的width是400,height是300,不过千万不要混淆了,mark元素中的path使用的坐标是viewBox设置的新的用户坐标系。 该属性另外一个取值userSpaceOnUse,代表属性markerWidth,markerHeight和marker的内容使用引用该marker的图形元素的坐标系统。
  • refX,refY:定义了引用的点与marker对齐的位置坐标。例如上面的例子中,引用的点是终点,要把它对齐到marker的(0,5)位置。注意refX,refY使用的是经过viewBox变换过的最终用户坐标系。
  • markerWidth,markerHeight:marker视窗的宽和高,这个很好理解。
  • orient:定义了marker旋转的角度。可以指定一个角度或者直接赋值auto。auto代表x轴正方向按照下列规则旋转:
    • 如果marker所在的点只属于一个path,则marker的x轴正向与path走向相同。参看上面例子。
    • 如果marker所在的点属于两个不同的path,则marker的x轴正向与两个path的夹角的角等分线走向一致。

图形元素的marker属性。图形元素要引用一个marker则需要使用相关的属性,从上面的例子中也可以看到marker的用法。主要是这3个:

  • marker-start(把引用的marker放到起点),
  • marker-mid(把引用的marker放到除起点和终点外的所有点),
  • marker-end(把引用的marker放到终点)。这3个属性的取值可能是none(代表不引用marker),marker的引用(引用某marker),inherit(这个不用多说了)。

脚本与样式 - script元素与style元素

实际上,基本上所有的属性(对于所有元素,不仅是文本)都可以用CSS与一个元素关联,并且所有CSS属性都在SVG图像中可用。可以直接用样式属性设计元素的样式,或者引用样式表设计元素的样式。对XML文件来说不应该解析样式表(因为它们偶尔包含会引起问题的字符),因此需要将它们置于XML CDATA节。脚本也是同样的道理,需要放到XML CDATA节中。看下面的CSS例子:

<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">   
  <desc>Text</desc> <defs>   
    <style type="text/css">      
      <![CDATA[      
        .abbreviation { text-decoration: underline; }      
      ]]>                     
    </style> 
  </defs>   
  <g>      
    <text x="20" y="50" font-size="30">Colors can be specified</text>     
    <text x="20" y="100" font-size="30">by their        
      <tspan fill="rgb(255,0,0)" class="abbreviation">R</tspan>        
      <tspan fill="rgb(0,255,0)" class="abbreviation">G</tspan>        
      <tspan fill="rgb(0,0,255)" class="abbreviation">B</tspan>     values</text>     
    <text x="20" y="150" font-size="30">or by keywords such as</text>     
    <text x="20" y="200">        
       <tspan style="fill: lightsteelblue; font-size:30">lightsteelblue</tspan>,     
    </text>
  </g>
</svg>

再看脚本的例子:

<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
 <desc>Scripting the onclick event</desc>
 <defs>
    <script type="text/ecmascript"> 
    <![CDATA[
      function hideReveal(evt) {
        var imageTarget = evt.target;
        var theFill = imageTarget.getAttribute("fill");
        if (theFill == 'white')
          imageTarget.setAttribute("fill", "url(#notes)");
        else
          imageTarget.setAttribute("fill", "white");
      }
    ]]> 
    </script>  
    <pattern id="notes" x="0" y="0" width="50" height="75" 
                   patternTransform="rotate(15)" 
                   patternUnits="userSpaceOnUse">
         <ellipse cx="10" cy="30" rx="10" ry="5"/>
         <line x1="20" y1="30" x2="20" y2="0" 
                   stroke-width="3" stroke="black"/>
         <line x1="20" y1="0" x2="30" y2="5" 
                   stroke-width="3" stroke="black"/>
     </pattern>
   </defs>
 <ellipse onclick="hideReveal(evt)" cx="175" cy="100" rx="125" ry="60"
           fill="url(#notes)" stroke="black" stroke-width="5"/>
</svg>

条件处理 - switch元素

条件处理属性是能控制所在元素渲染与否的属性。基本上大多数的元素(特别是图形元素)都可以指定条件处理属性。条件处理属性有3个:requiredFeatures, requiredExtensions和systemLanguage。这些属性就是一组测试,都允许指定一个值列表(前面两个属性是空格隔开的,语言这个属性是使用逗号隔开的),默认值都为true。

SVG的switch元素提供了按指定条件渲染的能力。switch元素是一个容器元素,可以包含图形元素,解释性元素,动画元素,a, foreignObject,g,image,svg,switch,text,use等元素。switch元素会按顺序检查直接子元素的条件处理属性,然后渲染满足自身条件的的第一个子元素,其他的子元素都会被忽略。这些属性与display属性一样,只会影响直接使用这些属性的元素的渲染,不会影响引用的元素(比如use引用的元素)。简单的说,这3个属性会影响a, altGlyph, foreignObject, textPath, tref, tspan ,animate, animateColor, animateMotion, animateTransform, set等元素,不会影响defs,cursor, mask, clipPath, pattern等元素(这些元素那么不是可渲染的,要么就是引用别的元素)。

注意:子元素的display和visibility属性值并不影响switch元素条件判断的结果。

条件处理属性的取值列表参看官方文档,这里就看一个小例子:

<switch> 
    <rect   requiredFeatures= "http://www.w3.org/TR/SVG11/feature#Filter "   
         x= "10 "   y= "10 "   width= "322 "   height= "502 "   opacity= "0.6 " 
         fill= "black "   stroke= "none "   filter= "url(#gblurshadow) "   /> 
    <rect   x= "10 "   y= "10 "   width= "322 "   height= "502 "   opacity= "0.6 " 
            fill= "black "   stroke= "none "   /> 
</switch> 

这个例子的意思简单的说就是:使用的浏览器支持filter特性,那么就绘制上面的矩形(带filter属性),如果不支持filter特性,就绘制下面的矩形。

其实更多的时候,用的比较多的属性是systemLanguage,就是文本的多语言处理能力。例如:

<svg xmlns="http://www.w3.org/2000/svg"
     version="1.1" width="5cm" height="5cm">
  <switch> 
    <text x='10' y='20' systemLanguage="de">de - HAHA</text> 
    <text x='10' y='20' systemLanguage="en">en - haha</text> 
  </switch> 
</svg>

蒙板

SVG支持多种蒙板特效,使用这些特性,我们可以做出很多很炫的效果。至于中文中把mask叫做"蒙板"还是"遮罩"就不去区分了,这里都叫做蒙板吧。

SVG支持的蒙板类型:

  • 裁剪路径(cliping path)。裁剪路径是由path, text或者基本图形组成的图形。所有在裁剪路径内的图形都可见,所有在裁剪路径外的图形都不可见。
  • 遮罩/蒙板(mask)。蒙板是一种容器,它定义了一组图形并将它们作为半透明的媒介,可以用来组合前景对象和背景。

裁剪路径和其他的蒙板一个重要的区别就是:裁剪路径是1位蒙板,也就是说裁剪路径覆盖的对象要么就是全透明(可见的,位于裁剪路径内部),要么就是全不透明(不可见,位于裁剪路径外部)。而蒙板可以指定不同位置的透明度。

视窗的裁剪路径 - overflow和clip属性

HTML元素的overflow属性和clip属性共同设置了该元素对内容的剪裁行为。同样的,在SVG中,这2个属性还可以使用。

overflow = visible | hidden | scroll | auto | inherit

overflow属性定义了当元素的内容超过元素的边框的时候采取的行为。

这个属性可以用于能创建新视窗的元素(svg,symbol,image,foreignObject),pattern和marker元素。这个属性的取值含义如下:

  • visible:显示所有内容,即使是内容已经在元素的边框外边,这个是默认值。
  • hidden:隐藏超出裁剪路径的内容。裁剪路径由clip属性指定。
  • scroll:采用滚动条的形式,呈现超出的内容。
  • auto:采用浏览器定义的行为,这个似乎不太可靠。

这个属性和CSS2中的同名属性基本相同,只不过在SVG中,有一些不同的处理过程:

  1. overflow属性对于除了创建新视窗的元素(svg,symbol,image,foreignObject),pattern和marker元素外的元素都没有效果。
  2. 裁剪路径与视窗是对应的,创建了新的视窗,就创建了新的裁剪路径。默认的裁剪路径就是视窗边界。

clip = <shape> | auto | inherit

clip属性用于设置当前视窗的裁剪路径。

这个属性可以用于能创建新视窗的元素(svg,symbol,image,foreignObject),pattern和marker元素。这个属性和CSS2中同名属性有一样的参数。auto代表裁剪路径与视窗边框是一致的。当使用图形作为参数时(设置裁剪矩形的top,right,bottom和left的值), 可以使用用户坐标值(即不带单位的坐标)。例如:

P { clip: rect(5px, 10px, 10px, 5px); }

这里注意,默认情况下(overflow和clip都取默认值),裁剪路径是与视窗的边框是一致的。当设置了viewBox和preserveAspectRatio以后,通常也需要把clip裁剪路径的四边映射成viewBox的四边,这样才能保证某些显示效果还是一样的(当然如果都是默认值,就不用设了)。

对象的裁剪路径 - clipPath元素

裁剪路径使用clipPath元素定义,然后使用clip-path属性引用。

clipPath可以包含path元素,text元素,基本的图形元素(circle等)和use元素。如果是use元素,则它必须是直接引用path,text或者基本图形元素,不能引用的是其他的元素。

注意裁剪路径只是一位的遮罩层,该路径是包含的所有的元素的并集。在这个集合中的对象就可以显示,不在这个范围内的对象就不显示。具体判定点在不在范围内的算法由"clip-rule"属性指定。

对于图形对象,裁剪路径等于自己clip-path设置的裁剪路径与所有外层元素的裁剪路径(包括clip-path和overflow设置的裁剪路径)的并集。注意几点:

  1. clipPath元素自身并不会从外层节点继承clipPath定义的裁剪路径。
  2. clipPath元素自身可以设置clip-path属性。效果是两个路径的交集。
  3. clipPath元素的子元素可以设置clip-path属性:效果是两个路径的并集。
  4. 空裁剪路径会裁掉元素内所有的内容。

下面看看几种重要的属性:

clipPathUnits = "userSpaceOnUse(默认值) | objectBoundingBox"

这个属性定义了clipPath元素使用的坐标系统,这两个值我们都很熟悉了,分别是采用引用当前裁剪路径的元素的用户坐标系统和包围盒比例值。

clipPath元素从来不直接渲染,都是通过clip-path被引用,所以设置clipPath元素的display属性没有作用。

clip-path = "<url(#裁剪路径名)> | none inherit"

这个属性不用多说了,用于引用裁剪路径,这里需要注意的是,所有的容器元素,基本图形元素和clipPath元素都可以使用这个属性。

clip-rule = "nonzero(默认值) | evenodd | inherit"

这个属性用于确定哪些点是属于裁剪路劲内部的点。对于简单的封闭图形,这个很好判定,但是对于复杂的内部有洞的图形,就有区别了。这个属性的取值与fill-rule的取值含义是一样的:

nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:

例:

nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:

exp-0302.png

evenodd:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则改点在图形内,需要填充;个数为偶数则点在图形外,不需要填充。看下图的示例:

exp-0303.png

clip-rule属性只能用于clipPath元素的内部图形元素。例如下面的设置是起作用的:

<g>
  <clipPath id="MyClip">
    <path d="..." clip-rule="evenodd" />
  </clipPath>
  <rect clip-path="url(#MyClip)" ... />
</g>

如果元素不在clipPath中是不起作用的。例如下面的设置是不起作用的:

<g clip-rule="nonzero">
  <clipPath id="MyClip">
    <path d="..." />
  </clipPath>
  <rect clip-path="url(#MyClip)" clip-rule="evenodd" ... />
</g>

最后看裁剪路径的一个小例子:

<svg width="100px" height="100px">
  <g>
    <clipPath id="MyClip">
      <path d="M 10,10 L 10,20 L 20,20 L 20,10 Z" clip-rule="evenodd" />
    </clipPath>
  </g>
  <rect clip-path="url(#MyClip)" x="10" y="10" width="80" height="80" fill="Red" />
</svg>

矩形只有左上角10*10的区域是可见的。

蒙板- mask元素

在SVG中,你可以为渲染的对象指定任何的图形元素或者g元素作为蒙板,来将渲染对象组合到背景中。

蒙板用mask元素定义,使用蒙板的时候只需要在对象的mask属性中引用蒙板就可以了。

mask元素可以包含任何的图形元素和容器元素(例如g)。

蒙板的效果其实大家也比较清楚,基本就是根据蒙板中每个点的颜色和透明度计算出一个最终的透明度,然后在渲染对象的时候,在对象上面罩上这个带有不同透明度的蒙板层,体现出蒙板的遮挡效果。对于渲染对象来说,只有在蒙版内的部分会按照蒙板上点的透明度来渲染,不在蒙板内的部分不显示。看下面的例子:

<svg width="8cm" height="3cm" viewBox="0 0 800 300" version="1.1"
     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="Gradient" gradientUnits="userSpaceOnUse"
                    x1="0" y1="0" x2="800" y2="0">
      <stop offset="0" stop-color="white" stop-opacity="0" />
      <stop offset="1" stop-color="white" stop-opacity="1" />
    </linearGradient>
    <mask id="Mask" maskUnits="userSpaceOnUse"
          x="0" y="0" width="800" height="300">
      <rect x="0" y="0" width="800" height="300" fill="url(#Gradient)"  />
    </mask>
    <text id="Text" x="400" y="200" 
          font-family="Verdana" font-size="100" text-anchor="middle" >
      Masked text
    </text>
  </defs>

  <!-- 视窗的背景 -->
  <rect x="0" y="0" width="800" height="300" fill="#FF8080" />
  
  <!-- 第一步绘制一个带有蒙板的Text,可以看到蒙板的透明度效果已经应用到字上了.
       第二步是绘制一个不带蒙板的Text,来作为第一步Text的轮廓 -->
  <use xlink:href="#Text" fill="blue" mask="url(#Mask)" />
  <use xlink:href="#Text" fill="none" stroke="black" stroke-width="2" />
</svg>

效果如下图所示:

exp-0901.png

大家可以试着将上面mask元素中的rect元素的width改成500,你会看到Text的一部分不显示了,这就是因为那部分已经超出蒙板的范围了。这里其实也看到了,上面的裁剪路径只不过是一种特殊的蒙板(每个点的透明度要么是0,要么是1)。

蒙板的定义和使用已经介绍了,下面看几个重要的属性:

maskUnits = "userSpaceOnUse | objectBoundingBox(默认值)"

定义了mask元素中坐标(x,y)和长度(width,height)的坐标系统:使用引用该蒙板的元素的用户坐标系,或者是使用相对于引用蒙板的元素的包围盒的相对值。这个值的含义与前面章节中的单位含义是相同的。

maskContentUnits = "userSpaceOnUse(默认值) | objectBoundingBox"

定义了mask元素中子元素的坐标系统。

x, y, width, height

定义了蒙板的位置和大小,在默认的objectBoundingBox坐标下,默认值分别为-10%,-10%,120%,120%。

此外要注意:蒙板不会直接渲染,只会在引用的地方起作用,所以display,opacity等属性对于mask元素来说都是不起作用的。

滤镜

滤镜称得上是SVG最强大的功能了,它允许你给图形(图形元素和容器元素)添加各种专业软件中才有的滤镜特效。这样你就很容易在客户端生成和修改图像了。而且滤镜并没有破坏原有文档的结构,所以维护性也很好。

滤镜用filter元素定义;需要使用的时候,在需要滤镜效果的图形或容器上添加filter属性,引用相关滤镜即可。

滤镜元素包含很多的滤镜原子操作;每个原子操作在传入的对象上执行一个基本的图形操作,并产生图形输出。大多数的原子操作生成的结果基本都是一个RGBA图片。每个原子操作的输入既可以是源图形,也可以使其他原子操作的结果。所以引用滤镜效果的过程就是在源图形上应用相关的滤镜原子操作,最后生成一个新的图形并渲染。

当在容器上(例如g元素)使用filter属性的时候,滤镜效果会应用到容器中的所有元素。但是容器中的元素并不会直接渲染到屏幕,而是会被暂时存储起来。然后,图形命令会被当做处理引用的filter元素的过程的一部分被执行,这个时候才会去渲染。这是通过使用SourceGraphic和SourceAlpha来指定的。下面的第二个例子中的第三种情况会演示这种效果。

有些滤镜效果会生成一些没有定义的像素点,这些点会被处理成透明效果。

先来看一个例子:

<svg width="7.5cm" height="5cm" viewBox="0 0 200 120"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>SVG 滤镜效果示例</title>
  <desc>采用各种滤镜效果实现一副图形的3D的光照效果。</desc>
  <defs>
    <filter id="MyFilter" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="120">
      <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
      <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
      <feSpecularLighting in="blur" surfaceScale="5" specularConstant=".75" 
                          specularExponent="20" lighting-color="#bbbbbb"  
                          result="specOut">
        <fePointLight x="-5000" y="-10000" z="20000"/>
      </feSpecularLighting>
      <feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut"/>
      <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" 
                   k1="0" k2="1" k3="1" k4="0" result="litPaint"/>
      <feMerge>
        <feMergeNode in="offsetBlur"/>
        <feMergeNode in="litPaint"/>
      </feMerge>
    </filter>
  </defs>
  <rect x="1" y="1" width="198" height="118" fill="#888888" stroke="blue" />
  <g filter="url(#MyFilter)" >
 <g>
      <path fill="none" stroke="#D90000" stroke-width="10" 
            d="M50,90 C0,90 0,30 50,30 L150,30 C200,30 200,90 150,90 z" />
      <path fill="#D90000" 
            d="M60,80 C30,80 30,40 60,40 L140,40 C170,40 170,80 140,80 z" />
      <g fill="#FFFFFF" stroke="black" font-size="45" font-family="Verdana" >
        <text x="52" y="76">SVG</text>
      </g>
    </g>
  </g>
</svg>

exp-1001.png

注意:在其他的浏览器中可能会有一些不同。

这个滤镜使用了6个效果,依次是(单步效果图如上图所示:):

  1. feGaussianBlur:这一步是进行高斯模糊处理;该特效的输入是源图片的透明度值,输出存到了临时缓冲blur中。blur值作为了下面feOffset和feSpecularLighting的输入。
  2. feOffset:这一步是把图片平移一些位置;该特效的输入是上一步中生成的blur,生成一个新的缓存offsetBlur。
  3. feSpecularLighting:这一步是把图片的表面进行光线的处理。输入是第一步中生成的blur,输出存放到新的缓存specOut中。
  4. 两次feComposite:这两步是对不同的缓存层进行组合。
  5. feMerge:这一步是合并不同的层。该步通常是最后的一步,融合各个缓存的层,生成最终的图片,并渲染呈现。虽然这一步也可以用多次feComposite特效完成,但是毕竟还是不太方便。

filter元素与滤镜效果区域

滤镜效果区域指的是滤镜效果起作用的区域。这个区域的大小是由filter元素下列的属性定义的:

filterUnits = "userSpaceOnUse | objectBoundingBox"

这个属性定义了x,y,width和height使用的坐标空间。与其他的Unit相关的属性一样,该属性也是两个值:

  • userSpaceOnUse和objectBoundingBox(默认值)。
  • userSpaceOnUse表示使用引用该filter元素的元素的用户坐标系统。
  • objectBoundingBox表示使用引用该filter元素的元素的包围盒的百分比做取值范围。

x,y,width,height

这些属性定义了滤镜起作用的矩形区域。滤镜效果不会应用在超过这个区域的点上。x,y的默认值是-10%,width与height的默认值是120%。

filterRes

该属性定义了中间缓存区域的大小,所以也定义了缓存图片的质量。通常情况下,不需要提供这个值,浏览器自己会选取合适的值。通常,滤镜效果区域应该定义成和背景正好能点和点一一对应,这样会带来一定的效率优势。

除了这些属性,filter元素的下列属性也很重要:

primitiveUnits = "userSpaceOnUse | objectBoundingBox"

这个属性定义每个原子操作中坐标和长度使用的坐标空间,这个属性的取值还是userSpaceOnUse和objectBoundingBox。只不过默认值是userSpaceOnUse。

xlink:href = "<iri>"

该属性用于在当前filter元素中引用其他的filter元素。

值得注意的是,filter元素只会继承自己的父节点的属性,并不会继承引用该filter元素的元素的属性。

滤镜总览

各种滤镜原子操作就不详述了,需要的时候查看官方文档即可。下面看一下这些操作的共性。除了"in"属性,下面的其他属性是所有原子操作都可用的。

x,y,width,height

这几个属性不多说了,它定义了滤镜原子起作用的区域,不妨成为"滤镜子区域"吧。这几个属性是受filter元素的作用区域限制的,默认情况下,取值分别是0,0,100%,100%。这些原子的作用区域超过filter元素的作用区域都不起作用。

result

存放该步操作的结果。指定了result以后,同一个filter元素的其他后续操作都可以用in来指定其为输入。这个参看上面的例子就知道了。如果省略了这个值,则只能作为紧挨着的下一步操作的隐式输入,注意如果紧挨着的下一步操作已经用in指定了输入,则以in指定的为准。

in

表示该步操作的输入。省略in属性的话,将会默认使用前一步的结果作为本步的输入,如果省略的是第一步的in,则会使用"SourceGraphic"作为值(参看下面的说明)。in属性可以引用前面result存放的值,也可以指定下面6个特殊的值:

  • SourceGraphic:这个值代表使用当前的图形元素作为操作的输入。
  • SourceAlpha:这个值代表使用当前图形元素的Alpha值作为操作的输入。
  • BackgroundImage:这个值代表使用当前的背景截图作为操作的输入。
  • BackgroundAlpha:这个值代表使用当前的背景截图的Alpha值作为操作的输入。
  • FillPaint:这个值使用当前图形元素的fill属性的值作为操作的输入。
  • StrokePaint:这个值使用当前图形元素的stroke属性的值作为操作的输入。

这些值中 BackgroundImage和BackgroundAlpha可能比较难以理解,下面重点看看这两个值。

访问背景截图

通常情况下,我们可以直接使用引用filter元素的元素的背景截图作为filter效果的源图片。代表这种输入的取值是BackgroundImage和BackgroundAlpha,前一个包含颜色和Alpha值,后一个只包含Alpha值。为了支持这种使用方式,还需要在引用filter元素的元素上显式的开启这个特性,这需要设置元素的enable-background属性。

enable-background = "accumulate | new [ <x> <y> <width> <height> ] | inherit"

这个属性只能用于容器元素,它定义了如何去截取背景截图。

  • new值代表:允许该容器的子元素访问容器的背景截图,并且该容器的子元素会渲染到背景中和设备上。
  • accumulate是默认值,它的效果取决于上下文:如果父辈容器元素使用了enable-background:new的话,那么该容器的所有图形元素都会参与背景的渲染。否则,说明父辈容器没有准备截取背景截图,该元素的图形元素显示只显示在设备上。

下面的例子演示了这些取值的效果:

<svg width="13.5cm" height="2.7cm" viewBox="0 0 1350 270"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>使用背景截图的例子</title>
  <desc>下面几个例子解释了不同情况下背景截图的使用情况</desc>

  <defs>
    <filter id="ShiftBGAndBlur" 
            filterUnits="userSpaceOnUse" x="0" y="0" width="1200" height="400">
      <desc>这个滤镜舍弃了源图片,而是使用背景截图</desc>
      <feOffset in="BackgroundImage" dx="0" dy="125" />
      <feGaussianBlur stdDeviation="8" />
    </filter>
    <filter id="ShiftBGAndBlur_WithSourceGraphic" 
            filterUnits="userSpaceOnUse" x="0" y="0" width="1200" height="400">
      <desc>这个滤镜特效同时融合了背景截图和当前元素的图片</desc>
      <feOffset in="BackgroundImage" dx="0" dy="125" />
      <feGaussianBlur stdDeviation="8" result="blur" />
      <feMerge>
        <feMergeNode in="blur"/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>

  <g transform="translate(0,0)">
    <desc>第一幅是没加滤镜的效果</desc>
    <rect x="25" y="25" width="100" height="100" fill="red"/>
    <g opacity=".5">
      <circle cx="125" cy="75" r="45" fill="green"/>
      <polygon points="160,25 160,125 240,75" fill="blue"/>
    </g>
    <rect x="5" y="5" width="260" height="260" fill="none" stroke="blue"/>
  </g>

  <g enable-background="new" transform="translate(270,0)">
    <desc>第二幅是在容器上使用滤镜效果</desc>
    <rect x="25" y="25" width="100" height="100" fill="red"/>
    <g opacity=".5">
      <circle cx="125" cy="75" r="45" fill="green"/>
      <polygon points="160,25 160,125 240,75" fill="blue"/>
    </g>
    <g filter="url(#ShiftBGAndBlur)"/>
    <rect x="5" y="5" width="260" height="260" fill="none" stroke="blue"/>
  </g>

  <g enable-background="new" transform="translate(540,0)">
    <desc>第三幅是在内部容器上使用滤镜效果,注意与第二幅图的不同</desc>
    <rect x="25" y="25" width="100" height="100" fill="red"/>
    <g filter="url(#ShiftBGAndBlur)" opacity=".5">
      <circle cx="125" cy="75" r="45" fill="green"/>
      <polygon points="160,25 160,125 240,75" fill="blue"/>
    </g>
    <rect x="5" y="5" width="260" height="260" fill="none" stroke="blue"/>
  </g>

  <g enable-background="new" transform="translate(810,0)">
    <desc>第四幅是在图形元素上使用滤镜效果</desc>
    <rect x="25" y="25" width="100" height="100" fill="red"/>
    <g opacity=".5">
      <circle cx="125" cy="75" r="45" fill="green"/>
      <polygon points="160,25 160,125 240,75" fill="blue"
               filter="url(#ShiftBGAndBlur)"/>
    </g>
    <rect x="5" y="5" width="260" height="260" fill="none" stroke="blue"/>
  </g>

  <g enable-background="new" transform="translate(1080,0)">
    <desc>第五幅是在图形元素上使用不同的滤镜效果</desc>
    <rect x="25" y="25" width="100" height="100" fill="red"/>
    <g opacity=".5">
      <circle cx="125" cy="75" r="45" fill="green"/>
      <polygon points="160,25 160,125 240,75" fill="blue"
               filter="url(#ShiftBGAndBlur_WithSourceGraphic)"/>
    </g>
    <rect x="5" y="5" width="260" height="260" fill="none" stroke="blue"/>
  </g>
</svg>

效果图如下(第一排是最终效果图,第二排是滤镜效果):

exp-1002.png

这个例子包含5个部分:

  1. 第一组中的图片,没用使用任何滤镜效果。
  2. 第二组使用一样的图片,但是开启了使用背景的效果。
  3. 第三组使用一样的图片,但是在内部的容器了使用了滤镜效果。
  4. 第四组在内容容器的元素上使用了滤镜效果。
  5. 最后一组使用了与第四组相同的滤镜效果并合并源图片后生成的效果。

滤镜的概念其实很简单,只不过是每个效果的代码看起来比较复杂,其实我们试一下就很清楚了,不过由于不同的浏览器对SVG的支持程度都不一样,具体的效果还是自己试试后再使用吧。

动画

交互性

SVG拥有良好的用户交互性,例如:

  1. SVG能响应大部分的DOM2事件。
  2. SVG能通过cursor良好的捕捉用户鼠标的移动。
  3. 用户可以很方便的通过设置svg元素的zoomAndPan属性的值来实现缩放等效果。
  4. 用户可以很方便的把动画和事件结合起来,完成一些复杂的效果。

通过给SVG元素挂接事件,我们可以使用脚本语言方便的完成一些交互任务。SVG支持大部分的DOM2事件,例如:onfocusin, onfocusou, onclick, onmousedown, onmouseup, onmousemove, onmouseout, onload, onresize, onscroll等事件。

除了这些,SVG还提供了独有的动画相关的事件,比如:onroom,onbegin,onend,onrepeat等。

事件大家比较熟悉,就不多说了。

动画的方式

SVG采用的是使用文本来定义图形,这种文档结构非常适合于创建动画。要改变图形的位置、大小和颜色,只需要调整相应的属性就可以了。事实上,SVG有为各种事件处理而专门设计的属性,甚至很多还是专门为动画量身定做的。在SVG中,实现动画可以有下面几种方式:

  1. 使用SVG的动画元素。这个下面会重点介绍。
  2. 使用脚本。采用DOM操作启动和控制动画,这个已经是一门成熟的技术了,后面有一个小例子。
  3. SMIL(Synchronized Multimedia Integration Language)。这个有兴趣的请参考:http://www.w3.org/TR/2008/REC-SMIL3-20081201/。

下面的例子包含了SVG中几种最基本的动画:

<svg width="8cm" height="3cm"  viewBox="0 0 800 300"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>基本动画元素</desc>
  <rect x="1" y="1" width="798" height="298" 
        fill="none" stroke="blue" stroke-width="2" />
  <!-- 矩形位置和大小的动画 -->
  <rect id="RectElement" x="300" y="100" width="300" height="100"
        fill="rgb(255,255,0)"  >
    <animate attributeName="x" attributeType="XML"
             begin="0s" dur="9s" fill="freeze" from="300" to="0" />
    <animate attributeName="y" attributeType="XML"
             begin="0s" dur="9s" fill="freeze" from="100" to="0" />
    <animate attributeName="width" attributeType="XML"
             begin="0s" dur="9s" fill="freeze" from="300" to="800" />
    <animate attributeName="height" attributeType="XML"
             begin="0s" dur="9s" fill="freeze" from="100" to="300" />
  </rect>
  <!-- 创建新的用户坐标空间,所以text是从新的(0,0)开始,后续的变换都是针对新坐标系的 -->
  <g transform="translate(100,100)" >
    <!-- 下面使用了set去动画visibility,然后使用animateMotion,
 animate和animateTransform执行其他类型的动画 -->
    <text id="TextElement" x="0" y="0"
          font-family="Verdana" font-size="35.27" visibility="hidden"  > 
      It's alive!
      <set attributeName="visibility" attributeType="CSS" to="visible"
           begin="3s" dur="6s" fill="freeze" />
      <animateMotion path="M 0 0 L 100 100" 
           begin="3s" dur="6s" fill="freeze" />
      <animate attributeName="fill" attributeType="CSS"
           from="rgb(0,0,255)" to="rgb(128,0,0)"
           begin="3s" dur="6s" fill="freeze" />
      <animateTransform attributeName="transform" attributeType="XML"
           type="rotate" from="-30" to="0"
           begin="3s" dur="6s" fill="freeze" />
      <animateTransform attributeName="transform" attributeType="XML"
           type="scale" from="1" to="3" additive="sum"
           begin="3s" dur="6s" fill="freeze" />
    </text>
  </g>
</svg>

把这段代码放到html文档的body中运行一下就可以知道动画的效果。

动画元素的公共属性

第一类:指定目标元素和属性

xlink:href

这个应该是很熟悉了,指向执行动画的元素。这个元素的必须是在当前的SVG文档片段中定义的。如果没有指定这个属性的话,动画会应用到自己的父元素上。

attributeName = "<attributeName>"

这个属性指定了应用动画的属性。如果该属性有namespace的话(不要忘了,SVG本质是XML文档),这个namespace也要加上。例如下面的例子中分别给xlink起了不同的别名,这里animate指定属性的时候就带了namespace:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <title>Demonstration of the resolution of namespaces for animation</title>
  <g xmlns:a="http://www.w3.org/1999/xlink">
    <animate attributeName="a:href" xlink:href="#foo" dur="2s" to="two.png" fill="freeze"/>
  </g>
  <g xmlns:b="http://www.w3.org/1999/xlink" xmlns:xlink="http://example.net/bar">
    <image xml:id="foo" b:href="one.png" x="35" y="50" width="410" height="160"/>
  </g>
</svg>

attributeType = "CSS | XML | auto(默认值)"

这个属性指定了属性取值的命名空间,这几个值的含义如下:

  • CSS:代表attributeName指定的属性是CSS属性。
  • XML:代表attributeName指定的属性是XML默认命名空间下的属性(注意svg文档本质上是xml文档)。
  • auto:代表先在CSS属性中查找attributeName指定的属性,如果没找到,则在默认的XML命名空间下寻找该属性。

第二类:控制动画时间的属性

下列属性都是动画时间属性;它们控制了动画执行的时间线,包括如何开始和结束动画,是否重复执行动画,是否保存动画的结束状态等。

begin = "begin-value-list"

该属性定义了动画的开始时间。可以是分号分开的一系列时间值。也可以是一些其他触发动画开始的值。比如事件,快捷键等。

dur = Clock-value | "media" | "indefinite"

定义了动画的持续时间。可以设置为以时钟格式显示的值。也可以设置为下列两个值:

  • media:指定动画的时间为内部多媒体元素的持续时间。
  • indefinite:指定动画时间为无限。

时钟格式指的是下列这些合法的取值格式:

02:30:03    = 2 hours, 30 minutes and 3 seconds
50:00:10.25 = 50 hours, 10 seconds and 250 milliseconds
02:33   = 2 minutes and 33 seconds
00:10.5 = 10.5 seconds = 10 seconds and 500 milliseconds
3.2h    = 3.2 hours = 3 hours and 12 minutes
45min   = 45 minutes
30s     = 30 seconds
5ms     = 5 milliseconds
12.467  = 12 seconds and 467 milliseconds
00.5s = 500 milliseconds
00:00.005 = 5 milliseconds

end = "end-value-list"

定义了动画的结束时间。可以是分号分开的一系列值。

min = Clock-value | "media"

max = Clock-value | "media"

设置了动画持续时间的最大最小值。

restart = "always" | "whenNotActive" | "never"

设置了动画能否随时重新开始。always代表动画可以随时开始。whenNotActive代表只能在没播放的时候重新开始,比如前一次播放结束了。never表示动画不能重新开始。

repeatCount = numeric value | "indefinite"

设置了动画重复的次数。 indefinite代表无限重复。

repeatDur = Clock-value | "indefinite"

设置重复的总的动画时间。indefinite代表无限重复。

fill = "freeze" | "remove(默认值)"

设置了动画结束后元素的状态。freeze表示动画结束后元素停留在动画的最后状态。remove代表动画结束以后元素回到动画前的状态,这个是默认值。

第三类:定义动画值的属性

这些属性定义了被执行动画的属性的取值情况。其实是定义了关键帧和插值的一些算法。

calcMode = "discrete | linear(默认值) | paced | spline"

定义了动画插值的方式:

  • discrete:离散的,不插值;
  • linear:线性插值;默认是linear(线性插值),但是如果属性不支持线性插值,则会采用discrete插值方式。
  • paced:步长插值;
  • spline:样条插值。

values = "<list>"

定义了以分号分隔的动画关键帧的值列表。支持向量值。

keyTimes = "<list>"

定义了以分号分隔的动画关键帧的时间列表。这个和values是一一对应的。这个值是受插值算法影响的,如果是线性(linear)和样条插值(spline),则keyTimes的第一个值必须是0,最后一个值必须是1。对于离散(discrete)的不插值的方式,keyTimes的第一个值必须是0。对于步长插值方式,很显然是不需要keyTimes。而且如果动画的持续时间设置为indefinite,则忽略keyTimes。

keySplines = "<list>"

这个属性定义了样条插值(贝塞尔插值)时的控制点,显然只有在插值模式选择为spline才起作用。这个列表中的值取值范围是0到1。

from = "<value>" to = "<value>" by = "<value>"

定义动画属性的起始值,结束值和步长值。这里需要注意:如果values已经制定了相关的值,则任何的from/to/by值都会被忽略。

第四类:控制动画是否是增量式的属性

有时候,如果相关的值设置的不是绝对值,而是增量值是非常有用的,使用additive属性可以达到这个目的。

additive = "replace(默认值) | sum"

这个属性控制了动画是否是增量式的。sum表示动画会较大相关的属性值或者其他低优先级的动画上。replace是默认值,表示动画会覆盖相关的属性值或者其他低优先级的动画。看一个小例子:

<rect width="20px" ...>
  <animate attributeName="width" from="0px" to="10px" dur="10s"
           additive="sum"/>
</rect>

这个例子演示了矩形width递增的动画效果。

有时候,如果重复的动画结果是叠加起来的,也非常有用,使用accumulate属性可以达到这个目的。

accumulate = "none(默认值) | sum"

这个属性控制了动画效果是否是累积的。none是默认值,表示重复的动画不累积。sum表示重复的动画效果是累积的。对于单次执行的动画,该属性没有意义。看个小例子:

<rect width="20px" ...>
  <animate attributeName="width" from="0px" to="10px" dur="10s"
           additive="sum" accumulate="sum" repeatCount="5"/>
</rect>

这个例子演示了矩形的长度在每次迭代中都在增加。

动画元素小结

SVG提供了下列动画元素:

  1. animate元素 这个是最基本的动画元素,可以直接为相关属性提供不同时间点的值。
  2. set元素 这个是animate元素的简写形式,支持所有的属性类型,尤其是当对非数字型的属性(例如visibility)进行动画时很方便。set元素是非增量的,相关的属性对之无效。 to指定的动画结束值类型一定要符合属性的取值类型。
  3. animateMotion元素 路劲动画元素。这个元素大多数属性都和上面一样,只有下面几个稍微有点区别:
    • calcMode = "discrete | linear | paced | spline"这个属性的默认值不同,在该元素中默认的是paced。
    • path = "<path-data>"动画元素移动的路径,格式与path元素的d属性的值的格式是一致的。
    • keyPoints = "<list-of-numbers>"这个属性的值是一系列分号给开的浮点数值,每个值的取值范围是0~1。这些值代表了keyTimes属性指定的对应时间点移动的距离,这里距离具体是多少是由浏览器自己决定的。
    • rotate = <number> | auto | auto-reverse"这个属性指定了元素移动时旋转的角度。默认值是0,
      • 数字代表旋转的角度,
      • auto表示随着路劲的方向转动物体。
      • auto-reverse表示转向与移动方向相反的方向。
  4. 此外animateMotion元素的from,by,to,values的值都是坐标对组成的;x值与y值之间用逗号或空格分开,每个坐标对之间用分号隔开比如from="33,15"表示起点x坐标为33,y坐标为15。

指定运动路径的方式有两种:

  • 一种为直接给path属性赋值,
  • 一种为使用mpath元素作为animateMotionde的子元素指定路径。

如果同时使用这两种方式,则使用mpath元素优先级高。这两种方式优先级都比values,from,by,to高。

看一个小例子:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="5cm" height="3cm"  viewBox="0 0 500 300"
     xmlns="http://www.w3.org/2000/svg" version="1.1"
     xmlns:xlink="http://www.w3.org/1999/xlink" >
  <rect x="1" y="1" width="498" height="298"
        fill="none" stroke="blue" stroke-width="2" />

  <path id="path1" d="M100,250 C 100,50 400,50 400,250"
        fill="none" stroke="blue" stroke-width="7.06"  />
  <circle cx="100" cy="250" r="17.64" fill="blue"  />
  <circle cx="250" cy="100" r="17.64" fill="blue"  />
  <circle cx="400" cy="250" r="17.64" fill="blue"  />
  <path d="M-25,-12.5 L25,-12.5 L 0,-87.5 z"
        fill="yellow" stroke="red" stroke-width="7.06"  >
    <animateMotion dur="6s" repeatCount="indefinite" rotate="auto" >
       <mpath xlink:href="#path1"/>
    </animateMotion>
  </path>
</svg>

animateColor元素。颜色动画元素。这是一个过时的元素,基本上所有功能都可以用animate代替,所以还是不要用了。

animateTransform元素。变换动画元素。看看特殊的一些属性: type = "translate | scale | rotate | skewX | skewY" 这个属性指定了变换的类型,translate是默认值。 from,by和to的值相应的都是对应变换的参数,这个还是与前面讲的变换是一致的。values则是一组分号隔开的这样的值系列。

支持动画效果的元素和属性

基本上所有图形元素(path,rect,ellipse,text,image...),容器元素(svg, g, defs, use, switch, clipPath, mask...)都支持动画。基本上大多数的属性都支持动画效果。详细的说明请参看官方文档。

使用DOM实现动画

SVG动画也可以使用脚本完成,DOM的详细内容后面会介绍,这里简单看一个小例子:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="2cm" viewBox="0 0 400 200"
     xmlns="http://www.w3.org/2000/svg"
     onload="StartAnimation(evt)" version="1.1">
  <script type="application/ecmascript"><![CDATA[
    var timevalue = 0;
    var timer_increment = 50;
    var max_time = 5000;
    var text_element;
    function StartAnimation(evt) {
      text_element = evt.target.ownerDocument.getElementById("TextElement");
      ShowAndGrowElement();
    }
    function ShowAndGrowElement() {
      timevalue = timevalue + timer_increment;
      if (timevalue > max_time)
        return;
      // Scale the text string gradually until it is 20 times larger
      scalefactor = (timevalue * 20.) / max_time;
      text_element.setAttribute("transform", "scale(" + scalefactor + ")");
      // Make the string more opaque
      opacityfactor = timevalue / max_time;
      text_element.setAttribute("opacity", opacityfactor);
      // Call ShowAndGrowElement again <timer_increment> milliseconds later.
      setTimeout("ShowAndGrowElement()", timer_increment)
    }
    window.ShowAndGrowElement = ShowAndGrowElement
  ]]></script>
  <rect x="1" y="1" width="398" height="198"
        fill="none" stroke="blue" stroke-width="2"/>
  <g transform="translate(50,150)" fill="red" font-size="7">
    <text id="TextElement">SVG</text>
  </g>
</svg>

SVG DOM

使用脚本可以很方便的完成各种复杂的任务,也是完成动画和交互的一种主流方式。由于SVG是html的元素,所以支持普通的DOM操作,又由于SVG本质上是xml文档,所以也有一种特殊的DOM操作,大多称之为SVG DOM。当然了,由于目前IE不支持SVG,开发基于IE的SVG页面需要采用不同的方式。这部分的知识大家其实都很熟悉,下面只是简单的看一下。

HTML页面中的DOM操作

DOM大家应该很熟悉了,这里先看一个小例子:

<head>     
<style>         
 #svgContainer {             
  width: 400px;             
  height: 400px;             
  background-color: #a0a0a0;         
 }     
</style>     
<script>         
 function CreateSVG () {             
  var xmlns = "http://www.w3.org/2000/svg";             
  var boxWidth = 300;             
  var boxHeight = 300;              
  var svgElem = document.createElementNS (xmlns, "svg");             
  svgElem.setAttributeNS (null, "viewBox", "0 0 " + boxWidth + " " + boxHeight);             
  svgElem.setAttributeNS (null, "width", boxWidth);             
  svgElem.setAttributeNS (null, "height", boxHeight);             
  svgElem.style.display = "block";              
  var g = document.createElementNS (xmlns, "g");             
  svgElem.appendChild (g);             
  g.setAttributeNS (null, 'transform', 'matrix(1,0,0,-1,0,300)');                  
  // draw linear gradient             
  var defs = document.createElementNS (xmlns, "defs");             
  var grad = document.createElementNS (xmlns, "linearGradient");             
  grad.setAttributeNS (null, "id", "gradient");             
  grad.setAttributeNS (null, "x1", "0%");             
  grad.setAttributeNS (null, "x2", "0%");             
  grad.setAttributeNS (null, "y1", "100%");             
  grad.setAttributeNS (null, "y2", "0%");             
  var stopTop = document.createElementNS (xmlns, "stop");             
  stopTop.setAttributeNS (null, "offset", "0%");             
  stopTop.setAttributeNS (null, "stop-color", "#ff0000");             
  grad.appendChild (stopTop);             
  var stopBottom = document.createElementNS (xmlns, "stop");             
  stopBottom.setAttributeNS (null, "offset", "100%");             
  stopBottom.setAttributeNS (null, "stop-color", "#0000ff");             
  grad.appendChild (stopBottom);             
  defs.appendChild (grad);             
  g.appendChild (defs);                  
  // draw borders             
  var coords = "M 0, 0";             
  coords += " l 0, 300";             
  coords += " l 300, 0";             
  coords += " l 0, -300";             
  coords += " l -300, 0";              
  var path = document.createElementNS (xmlns, "path");             
  path.setAttributeNS (null, 'stroke', "#000000");             
  path.setAttributeNS (null, 'stroke-width', 10);             
  path.setAttributeNS (null, 'stroke-linejoin', "round");             
  path.setAttributeNS (null, 'd', coords);             
  path.setAttributeNS (null, 'fill', "url(#gradient)");             
  path.setAttributeNS (null, 'opacity', 1.0);             
  g.appendChild (path);              
  var svgContainer = document.getElementById ("svgContainer");             
  svgContainer.appendChild (svgElem);                  
 }      
</script> 
</head> 
<body onload="CreateSVG ()"> 
    <div id="svgContainer"></div> 
</body> 

发现了没,与普通的html元素的DOM操作完全一样:

  • 选择元素:document.getElementById
  • 创建元素:document.createElementNS
  • 创建子元素的另外一种方式:element.createChildNS
  • 添加元素:node.appendChild
  • 设置元素的属性:element.setAttributeNS/element.setAttribute

除了上面这几个操作,下面的操作和属性也很常见:

  • 获取元素的属性值: element.getAttributeNS/element.getAttribute
  • 检查元素是否存在某属性:element.hasAttributeNS
  • 移除元素的某属性:element.removeAttributeNS
  • 父元素、子元素和兄弟节点:element.parentNode/element.firstChild/child.nextSibling

这些方法这里不再详细介绍了;此外,DOM树的节点结构,对象之间的继承关系也都是差不多的,就不详述了。需要的同学参看后面的DOM Core Object的文档。

不过,需要注意的是SVG本质上是XML文档,所以基本采用的DOM方法都是带NS结尾的方式,来提供相关的namespace;如果创建元素时已经提供了namespace,而且没有多个namespace的问题,那么设置相关属性的时候,也可以选择使用不带NS的版本,比如直接使用element.setAttribute设置属性值,但是总的来说,还是强烈推荐使用带NS结尾的版本,因为这个版本总是工作正常的,即使是在多namespace的情况下。

SVG DOM

这个与标准的DOM有哪些不同,我也没找到什么全面的资料,目前只知道对属性的赋值方式是不同的。如果有了解这方面的同学还请吱一声啊。

上面的例子中,我们使用element.setAttributeNS/element.setAttribute来给属性赋值,在SVG DOM中,可以使用面向对象的方式,通过访问点号来给对象的属性赋值,比如下面是两种方式的对比:

普通的DOM方式:

element.setAttribute("x", "10");
element.setAttribute("y", "20");
element.setAttribute("width", "100%");
element.setAttribute("height", "2em");

而SVG DOM的方式:

element.x.baseVal.value = 10;
element.y.baseVal.value = 20;
element.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);
element.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_EMS, 10);

DOM脚本属于传统的脚本,其特征是通过构建「值字符串」来设置各个项。SVG DOM脚本样式的优点是,你不必构建「值字符串」,所以性能优于DOM脚本。

嵌入SVG的脚本

如果要在SVG内部添加脚本,就需要使用script元素,这个前面已经讲过了,除了这一点,基本上与把脚本放到外面的HTML中是一样的。看一个例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head>     
</head> 
<body> 
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
  <script type="text/ecmascript">
    <![CDATA[
      function showRectColor() {
        alert(document.getElementById("myBlueRect").getAttributeNS(null,"fill"));
      }
  
      function showRectArea(evt) {
        var width = parseFloat(evt.target.getAttributeNS(null,"width"));
        var height = parseFloat(evt.target.getAttributeNS(null,"height"));
        alert("The rectangle area is: " + (width * height));
      }
  
      function showRootChildrenNr() {
        alert("Nr of Children: "+document.documentElement.childNodes.length);
      }
    ]]>
  </script>
  <g id="firstGroup">
    <rect id="myBlueRect" width="100" height="50" x="40" y="20" fill="blue" onclick="showRectArea(evt)"/>
    <text x="40" y="100" onclick="showRectColor()">Click on this text to show rectangle color.</text>
    <text x="40" y="130">Click on rectangle to show rectangle area.</text>
    <text x="40" y="160" onclick="showRootChildrenNr()">Click on this text to show the number of child
     <tspan x="40" dy="20">elements of the root element.</tspan></text>
  </g>
</svg>
</body> 
</html> 

在这个例子中,列举了常见的获取DOM对象的方式:

  1. 通过document.getElementById或者document.getElementByClassName之类的方法获取对象;
  2. 通过document.documentElement或者document.rootElement获取document对象;
  3. 通过事件参数evt.target获取产生事件的对象。这种方式的优点就是不使用id就可以获取到产生事件的对象。

其余的脚本基本和普通的DOM是一样的。