一:简介
路径呈现一个图形的外边沿,可以被填充,可以作为笔画,可以作为裁剪路径,或者任意前面三种的组合。
一个路径可以使用一个当前点的概念来描述。类似于在纸上画画,这个当前点可以被认为是笔的位置。笔的位置可以被改变,并且一个图形的外边沿的轨迹将会通过拖动笔画出直线或者曲线形成。
路径会呈现一个物体的几何外边沿, 它是参照moveto,lineto,curveto,arc,closepath等元素。复合路径可以在物体上画出圆圈。
这一章节主要讲述SVG路径的语法,表现形式和DOM接口。SVG路径的各种实现接口可以参考:
‘path’ element implementation notes和
Elliptical arc implementation notes。
一个路径在SVG中是用"path"元素产生的。
二:"path"元素
DOM接口:
SVGPathElement
属性定义:
d="path data"
一个图形的外边沿定义。可以设置动画的。路径数据动画只有在动画规范当中每一个路径数据规范有都有准确相同的"d"属性值作为路径数据指令才有可能。如果一个动画已经被指示,但是路径数据指令不一致,那么动画指令就会发生错误。动画引擎将每个参数单独的插入到基于属性值的给定动画元素的每个路径数据动画。标记和布尔值将作为分数插入到0到1之间,并且任意的非零值将会被看做1或者true。
pathLength="<number>"
以用户的单位长度来估算整个路径的长度。这个值可以常来被用作校正用户代理自己的路径长度。用户代理将缩放所有的路径长度的估算通过这个原"pathLength"与用户代理自己所估算出的真实路径长度。"pathLength"可能影响路径上的文本,移动动画和边界操作等计算结果 。出现负值是错误的。可以设置动画的。
三:路径数据
1.路径数据概论
一个路径通过引入含有moveto,line,curve,arc,closepath说明的d属性的"path"元素导入而被定义的 。
下面是一个指定三角形路径的例子:
<?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="4cm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Example triangle01- simple example of a 'path'</title> <desc>A path that draws a triangle</desc> <rect x="1" y="1" width="398" height="398" fill="none" stroke="blue" /> <path d="M 100 100 L 300 100 L 200 300 z" fill="red" stroke="blue" stroke-width="3" />
</svg>
路径数据可以包含换行符,因此可以拆分成多行提高可读性。由于特定工具的行长度限制,推荐SVG创建者将长路径数据分隔成每行不超过255个字符的多行数据。同时注意换行符在路径数据中仅能在特定的位置使用。自从许多的SVG文件中路径数据占主导地位之后,为了满足最小的文件大小和有效地下载,路径数据是很简洁的。许多SVG尝试压缩路径数据的大小的方法如下:
a:所有的说明可以用一个字符来解释(例如:moveto可以用M代替)
b:多余的空白处和分隔要尽可能的压缩(例如“M 100 100 L 200 200”用“M100 100L200 200”代替)
c:当在一行当中相同的指令连续出现多次的时候,可以进行合并(例如"M 100 100 L 200 200 L300 400"可以用"M 100 100 L 200 200 300 400"代替)
d:所有命令的相对版本也是有效地(大写意味着绝对坐标,小写意味着相对坐标)
e:连续的横向或者竖向的路径可以进行最优化合并
f:控制点一致的连续弧线可以进行最优化合并
路径数据语法是一种前缀符号。唯一被允许使用的分界符号是".",其他的分界符号被禁止使用。(例如"13,000.56"是无效的数据,"13000.56"是正确的)
对于相对版本的命令,从命令开始的对当前点来说,所有的坐标值都是相对的。
在下面的表格当中,下面的记号表示:
():参数组
+:要求1个或者多个给定的参数或参数组
接下来将列出所有的指令
2."moveto"指令
这个"moveto"指令建立了一个新的当前点。它的作用就像是把笔提起,移动到一个新的位置。一个路径片段必须以"moveto"开始。随后的"moveto"指令将会作为一个子路径的起点。
命令
|
名称
|
参数
|
描述
|
M (absolute)
m (relative) |
moveto
|
(x y)+
|
从给定的(x,y)坐标开启一个新的子路径或路径。M表示后面跟随的是绝对坐标值。
m表示后面跟随的是一个相对坐标值。如果"moveto"指令后面跟随着多个坐标值,那么
这多个坐标值之间会被当做用线段连接。因此如果moveto是相对的,那么lineto也将会
是相对的,反之也成立。如果一个相对的moveto出现在path的第一个位置,那么它会
被认为是一个绝对的坐标。在这种情况下,子序列的坐标将会被当做相对的坐标,即便
它在初始化的时候是绝对坐标。
|
3."closepath"指令
一个闭合指令会结束当前的子路径,并且会自动添加结束点到起始点之间的线段。如果一个闭合路径后面紧跟着一个moveto指令,那么moveto将标识起始点作为下一个路径的起始点。如果闭合路径后面立即跟着其他的指令,那么其他子路径将以起始点作为子路径的起始点。
当一个路径通过closepath来关闭的,它将与通过lineto命令手动的关闭路径的表现是不同的。在闭合路径时使用closepath,那么最后的那个路径片段会使用当前值的stroke-linejoin的方式连接到起始点。如果是手动的使用lineto指令,那么最后的片段和第一个片段之间不会连接,而是使用当前值的stroke-linecap指令进行两边的遮盖。指令最后,新的路径关键点被设置成为当前路径的起始点。
指令
|
名称
|
参数
|
描述
|
Z or
z |
closepath
|
无参数
|
通过从当前点到当前路径的起始点画一条线段来关闭当前的路径。Z和z指令都没有参数,
但是它们有相同的效果。
|
4.lineto指令
下面不同的lineto指令都是从当前点画直线到新的点:
指令
|
名称
|
参数
|
描述
|
L (absolute)
l (relative) |
lineto
|
(x y)+
|
画一条从当前点到给定的(x,y)坐标,这个给定的坐标将变为新的当前点。L表示后面
跟随的参数将是绝对坐标值;l表示后面跟随的参数将是相对坐标值。可以通过指定一系
列的坐标来描绘折线。在命令执行后,新的当前点将会被设置成被提供坐标序列的最后
一个坐标。
|
H (absolute)
h (relative) |
horizontal lineto
|
x+
|
从(cpx,cpy)画一个水平线到(x,cpy)。H表示后面跟随的参数是绝对的坐标,h表示
后面跟随的参数是相对坐标。可以为其提供多个x值作为参数。在指令执行结束后,
最新
的当前点将是参数提供的最后值(x,cpy)
|
V (absolute)
v (relative) |
vertical lineto
|
y+
|
从当前点(cpx,cpy)到(cpx,y)画一条竖直线段。V表示后面的参数是绝对坐标
值,v表示后面跟着的参数是相对坐标值。可以供多个y值作为参数使用。在指令的最
后,根据最后的参数y值最新的当前点的坐标值是(cpx,y).
|
5.曲线指令
这里有三组画曲线的指令:
Cubic Bézier commands (
C,
c,
S and
s):一个含有一个起始点,一个结束点,两个控制点的立方体的贝塞尔曲线片段。
Quadratic Bézier commands (
Q,
q,
T and
t):一个含有一个起始点,一个结束点,一个控制点的二次的贝塞尔曲线片段。
Elliptical arc commands (
A and
a):一个椭圆的弧度曲线片段。
6.立方体的贝塞尔曲线指令
立方体的贝塞尔曲线指令如下所示:
指令
|
名称
|
参数
|
描述
|
C (absolute)
c (relative) |
curveto
|
(x1 y1 x2 y2 x y)+
|
在曲线开始的时候,用(x1,y1)作为当前点(x,y)的控制点,
在曲线结束的时候,
用(x2,y2)作为当前点的控制点,
画一段立方体的贝塞尔曲线。C表示
后面跟随的参数是绝对坐标值;
c表示后面跟随的参数是相对坐标值。可以为贝塞尔函数提供多个参数
值。在指令执行完毕后,最后的当前点将变为在贝塞尔函数中只用的
最后的(x,y)坐标值
|
S (absolute)
s (relative) |
shorthand/smooth curveto
|
(x2 y2 x y)+
|
从当前点(x,y)画一个立方体的贝塞尔曲线。相对于当前点,
第一个控制点被认为是前面命令的第二个控制点的反射。(如果
前面没有指令或者指令不是C, c, S 或者s,那么就认定当前点和
第一个控制点是一致的。)(x2,y2)是第二个控制点,控制
着曲线结束时的变化。S表示后面跟随的参数是绝对的坐标值。
s表示后面跟随的参数是相对的坐标值。多个值可以作为
贝塞尔函数的参数。在执行执行完后,最新的当前点是在贝塞尔函数中
使用的最后的(x,y)坐标值。
|
立方体的贝塞尔曲线示例:用一个简单的曲线来展示一些简单使用立方体的贝塞尔曲线指令。这个例子使用内置的CSS样式来设置样式属性。
注意S指令的控制点是通过先前C指令的控制点的反射计算出来的作为S指令的起始参数。
<?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="4cm" viewBox="0 0 500 400" xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Example cubic01- cubic Bézier commands in path data</title> <desc>Picture showing a simple example of path data using both a "C" and an "S" command, along with annotations showing the control points and end points</desc> <style type="text/css"><![CDATA[ .Border { fill:none; stroke:blue; stroke-width:1 } .Connect { fill:none; stroke:#888888; stroke-width:2 } .SamplePath { fill:none; stroke:red; stroke-width:5 } .EndPoint { fill:none; stroke:#888888; stroke-width:2 } .CtlPoint { fill:#888888; stroke:none } .AutoCtlPoint { fill:none; stroke:blue; stroke-width:4 } .Label { font-size:22; font-family:Verdana } ]]></style> <rect class="Border" x="1" y="1" width="498" height="398" /> <polyline class="Connect" points="100,200 100,100" /> <polyline class="Connect" points="250,100 250,200" /> <polyline class="Connect" points="250,200 250,300" /> <polyline class="Connect" points="400,300 400,200" /> <path class="SamplePath" d="M100,200 C100,100 250,100 250,200 S400,300 400,200" /> <circle class="EndPoint" cx="100" cy="200" r="10" /> <circle class="EndPoint" cx="250" cy="200" r="10" /> <circle class="EndPoint" cx="400" cy="200" r="10" /> <circle class="CtlPoint" cx="100" cy="100" r="10" /> <circle class="CtlPoint" cx="250" cy="100" r="10" /> <circle class="CtlPoint" cx="400" cy="300" r="10" /> <circle class="AutoCtlPoint" cx="250" cy="300" r="9" /> <text class="Label" x="25" y="70">M100,200 C100,100 250,100 250,200</text> <text class="Label" x="325" y="350" style="text-anchor:middle">S400,300 400,200</text> </svg>
效果图像:
用SVG查看效果图 (只能使用可以查看SVG的浏览器才可以正常查看)
从效果图中可以看出,S指令的第一个控制点是前面C指令第二个控制点关于C指令当前点的反射点,这个说明S是C的简化。
下面的图像展示了贝塞尔曲线随着控制点变化发生的情况。前面五个例子图解说明了一个单独的立方体的贝塞尔曲线片段。最后一个例子展示了C指令后面S指令的情形。
7.二次贝塞尔曲线指令
二次贝塞尔曲线指令如下所示:
指令
|
名称
|
参数
|
描述
|
Q (absolute)
q (relative)
|
quadratic Bézier curveto
|
(x1 y1 x y)+
|
从当前点(x,y)开始,以(x1,y1)为控制点,画出一个二次贝塞尔曲线。
Q表
示后面跟随的参数是绝对坐标值,q表示后面跟随的参数是相对坐标值。
可以为贝塞尔函数指定
多个参数值。在指令执行结束后,新的当前点是贝塞尔曲线
调用参数中最后一个坐标值
(x,y)。
|
T (absolute)
t (relative)
|
Shorthand/smooth quadratic Bézier curveto
|
(x y)+
|
画一个从当前点到(x,y)的二次贝塞尔曲线。控制点被认为是上一个
指令的控制点以当前点为中心的对称点。(如果前面没有指令或者前面
的指令不是Q, q, T或者t,那么
控制点被认为是当前点
)。T表示后面跟随
的参数是绝对坐标值;t表示后面跟随的参数是相对坐标值。在指令执行结束后,
新的当前点被认定为最后传递给贝塞尔函数的坐标值(x,y)。
|
下面的例子展示了如何简单的使用二次贝塞尔曲线。注意T指令的控制点来源于前面Q指令控制点以T指令当前点的对称点。
<?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="12cm" height="6cm" viewBox="0 0 1200 600"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example quad01 - quadratic Bézier commands in path data</title>
<desc>Picture showing a "Q" a "T" command,
along with annotations showing the control points
and end points</desc>
<rect x="1" y="1" width="1198" height="598"
fill="none" stroke="blue" stroke-width="1" />
<path d="M200,300 Q400,50 600,300 T1000,300"
fill="none" stroke="red" stroke-width="5" />
<!-- End points -->
<g fill="black" >
<circle cx="200" cy="300" r="10"/>
<circle cx="600" cy="300" r="10"/>
<circle cx="1000" cy="300" r="10"/>
</g>
<!-- Control points and lines from end points to control points -->
<g fill="#888888" >
<circle cx="400" cy="50" r="10"/>
<circle cx="800" cy="550" r="10"/>
</g>
<path d="M200,300 L400,50 L600,300
L800,550 L1000,300"
fill="none" stroke="#888888" stroke-width="2" />
</svg>
生成图像结果:
8椭圆弧曲线指令
椭圆弧曲线指令如下所示:
指令
|
名称
|
参数
|
描述
|
A (absolute)
a (relative)
|
elliptical arc
|
(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
|
用来从当前点(x,y)来画出一个椭圆弧曲线。曲线的形状和方向通过椭圆半径(rx,ry)
和一个沿X轴旋转度来指明椭圆作为一个整体在当前坐标系下旋转的情形。椭圆的中心
(cx,cy)是通过满足其他参数的约束自动计算出来的。
large-arc-flag和sweep-flag
决定了计算和帮助要画的弧度大小。
|
参数具体详解rx ry:一个椭圆的x轴半径和y轴半径
x-axis-rotation:椭圆的X轴与当前坐标系X轴之间的角度
large-arc-flag :弧线选择是大弧度的曲线还是小弧度的曲线,1标识大的弧线,0标识小的弧线
sweep-flag:从当前点到(x,y)的弧线是顺时针的还是逆时针的,逆时针为0,顺时针为1
x y:指令的目标点
下面的例子展示了在一段路径中使用指令的情况:
<?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="12cm" height="5.25cm" viewBox="0 0 1200 400" xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Example arcs01 - arc commands in path data</title> <desc>Picture of a pie chart with two pie wedges and a picture of a line with arc blips</desc> <rect x="1" y="1" width="1198" height="398" fill="none" stroke="blue" stroke-width="1" /> <path d="M300,200 h-150 a150,150 0 1,0 150,-150 z" fill="red" stroke="blue" stroke-width="5" /> <path d="M275,175 v-150 a150,150 0 0,0 -150,150 z" fill="yellow" stroke="blue" stroke-width="5" /> <path d="M600,350 l 50,-25 a25,25 -30 0,1 50,-25 l 50,-25 a25,50 -30 0,1 50,-25 l 50,-25 a25,75 -30 0,1 50,-25 l 50,-25 a25,100 -30 0,1 50,-25 l 50,-25" fill="none" stroke="red" stroke-width="5" /> </svg>
SVG图像的结果如下所示:
椭圆弧曲线需要满足下面的约束条件之后才可以画出一个椭圆的片段:
弧线从当前点开始
弧线以(x,y)为结束点
椭圆有两个半径,(rx,ry)
椭圆的x轴相对于当前坐标系的x轴通过X轴旋转度旋转
在大多数情况下有4个不同的弧曲线会满足约束条件。large-arc-flag和 sweep-flag决定了这四个当中哪一个被画出,如下所示:
在这四段弧线当中,两段将比其余的两段大180度或者等于180度。剩余的两段比前面的两段小180度或者等于180度。如果large-arc-flag是1,表示两个比较大的弧线被选择出来;否则如果是0,那么较小的弧线被选择出来。
如果sweep-flag是1,那么弧线将会以一个合适的通过(椭圆公式x=cx+rx*cos(theta)和y=cy+ry*sin(theta))评测出一个当前点起始,逐步增长到(x,y)结束的角度描绘出来。值为0会导致弧线会在负角度方向被画出。(theta从当前点开始并增长到(x,y)结束弧线。)
下面是large-arc-flag和sweep-flag的组合图解,并展示了基于四种不同结果的图形。对每个情况,可以使用下面的指令:
<path d="M 125,75 a100,50 0 ?,? 100,50" style="fill:none; stroke:red; stroke-width:6"/>
当里面的"?,?"被替换为"0,0" "0,1" "1,0" "1,1"时会形成四种可能的结果:
在这些圆弧当中,"0,0"与"1,1"会是在一个椭圆中;"0,1"与"1,0"在一个椭圆中。
具体的实现和注意事项可以参考:
Elliptical arc implementation notes
9路径数据的语法
下面的记号是用在路径数据语法的巴科斯诺尔范式形式:
*:0个或者多个
+:一个或者多个
?:0个或者一个
():分组
|:可选择项的分隔
双引号围绕着文字值
下面是SVG路径的BNF范式:
svg-path:
wsp* moveto-drawto-command-groups? wsp*
moveto-drawto-command-groups:
moveto-drawto-command-group
| moveto-drawto-command-group wsp* moveto-drawto-command-groups
moveto-drawto-command-group:
moveto wsp* drawto-commands?
drawto-commands:
drawto-command
| drawto-command wsp* drawto-commands
drawto-command:
closepath
| lineto
| horizontal-lineto
| vertical-lineto
| curveto
| smooth-curveto
| quadratic-bezier-curveto
| smooth-quadratic-bezier-curveto
| elliptical-arc
moveto:
( "M" | "m" ) wsp* moveto-argument-sequence
moveto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? lineto-argument-sequence
closepath:
("Z" | "z")
lineto:
( "L" | "l" ) wsp* lineto-argument-sequence
lineto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? lineto-argument-sequence
horizontal-lineto:
( "H" | "h" ) wsp* horizontal-lineto-argument-sequence
horizontal-lineto-argument-sequence:
coordinate
| coordinate comma-wsp? horizontal-lineto-argument-sequence
vertical-lineto:
( "V" | "v" ) wsp* vertical-lineto-argument-sequence
vertical-lineto-argument-sequence:
coordinate
| coordinate comma-wsp? vertical-lineto-argument-sequence
curveto:
( "C" | "c" ) wsp* curveto-argument-sequence
curveto-argument-sequence:
curveto-argument
| curveto-argument comma-wsp? curveto-argument-sequence
curveto-argument:
coordinate-pair comma-wsp? coordinate-pair comma-wsp? coordinate-pair
smooth-curveto:
( "S" | "s" ) wsp* smooth-curveto-argument-sequence
smooth-curveto-argument-sequence:
smooth-curveto-argument
| smooth-curveto-argument comma-wsp? smooth-curveto-argument-sequence
smooth-curveto-argument:
coordinate-pair comma-wsp? coordinate-pair
quadratic-bezier-curveto:
( "Q" | "q" ) wsp* quadratic-bezier-curveto-argument-sequence
quadratic-bezier-curveto-argument-sequence:
quadratic-bezier-curveto-argument
| quadratic-bezier-curveto-argument comma-wsp?
quadratic-bezier-curveto-argument-sequence
quadratic-bezier-curveto-argument:
coordinate-pair comma-wsp? coordinate-pair
smooth-quadratic-bezier-curveto:
( "T" | "t" ) wsp* smooth-quadratic-bezier-curveto-argument-sequence
smooth-quadratic-bezier-curveto-argument-sequence:
coordinate-pair
| coordinate-pair comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence
elliptical-arc:
( "A" | "a" ) wsp* elliptical-arc-argument-sequence
elliptical-arc-argument-sequence:
elliptical-arc-argument
| elliptical-arc-argument comma-wsp? elliptical-arc-argument-sequence
elliptical-arc-argument:
nonnegative-number comma-wsp? nonnegative-number comma-wsp?
number comma-wsp flag comma-wsp? flag comma-wsp? coordinate-pair
coordinate-pair:
coordinate comma-wsp? coordinate
coordinate:
number
nonnegative-number:
integer-constant
| floating-point-constant
number:
sign? integer-constant
| sign? floating-point-constant
flag:
"0" | "1"
comma-wsp:
(wsp+ comma? wsp*) | (comma wsp*)
comma:
","
integer-constant:
digit-sequence
floating-point-constant:
fractional-constant exponent?
| digit-sequence exponent
fractional-constant:
digit-sequence? "." digit-sequence
| digit-sequence "."
exponent:
( "e" | "E" ) sign? digit-sequence
sign:
"+" | "-"
digit-sequence:
digit
| digit digit-sequence
digit:
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
wsp:
(#x20 | #x9 | #xD | #xA)
一个BNF的处理必须尽可能的满足一个给定的BNF产品,并在直到一个字符不满足这个产品的要求时停止。因此在字符串"M100-200"中,moveto指令被认为是100,因为因为负号不可以跟随在一个数字之后成为一个坐标值。moveto指令的第二个参数会是-200.类似的,对于字符串"M0.6.5",第一个坐标值是0.6,解析因为遇到第二个小数点而停止,因为坐标值只允许有一个小数点。那么第二个坐标值将是".5"。
注意:BNF范式允许路径的"d"属性是一个空值。这不会出错,但是他不能展示一个路径。
四:沿路径的长度
各种各样的操作,例如在路径上的文本和路径动画以及各种边界操作,这些都需要用户来计算沿着一个几何图形的长度,例如"path"。
精确地数学公式可以计算沿着路径的长度,但是这些数学公式非常的复杂并需要大量的计算。这会提示制作产品或者用户使用算法来获取尽可能精确地结果。但是,为了尽可能的兼容不同的实现和帮助计算出近似的合乎用户目的的长度结果,"pathLength"属性被用来保存用户的对整个路径长度的记过,以便于用户可以通过用户自己计算出的路径长度比率"pathLength"来缩放沿整个路径的长度。
当一个"path"内只有moveto指令操作时,路径长度为0.只有各种lineto,curveto,arcto指令才会启动长度计算。