1

看d3.js的时候就见识过<path>元素有多牛X,啥都能画出来,可不知其原理的话,看上去简直形同天书!没关系,有的是时间精力和耐性,仔细看看这天书的正确打开方式。

把<path>看做是一支笔,可以按照我的意愿在哪儿落“笔”,画各种各样在其功能范围内的线条,还能自动帮我做很多事情来美化这些线条。所有这些控制这支“笔”的信息被包含在<path>元素的"d"

落笔(M)

以moveTo首字母M开头,加上坐标,以逗号或空格分割,表示移动到指定坐标位置。

<path d="M 10 10"></path>

直线(L)

以lineTo首字母L开头,加上坐标,以逗号或空格分割,表示从当前位置画一条直线到指定坐标位置。

<path d="M 10 10 L 50 10"></path>

可连续画多条直线

<path d="M 10 10 L 50 10 L 50 50 L 10 50 L 10 10">

这就近似画了个矩形,为什么说近似,我们加上线的宽度看一下

<g fill="none" stroke="black" stroke-width="5">
    <path d="M 10 10 L 50 10 L 50 50 L 10 50 L 10 10"></path>
</g>

clipboard.png

左上角的残缺说明,即使直线的开始和末尾的坐标重合,svg也不会对重合处做自动衔接处理,咋办?

封闭(Z)

用Z代表closePath,表示让svg自动将前一个M指定的起始位置与Z之前最后一个坐标位置用直线连接。

<path d="M 10 10 L 50 10 L 50 50 L 10 50 L 10 10 Z"></path>

现在就真的是个矩形了。

可以利用多个M在同一个path中画出多余一个不相连的图形。

<g fill="none" stroke="black" stroke-width="5">
   <path d="M 10 10 L 50 10 L 50 50 L 10 50 Z
        M 10 80 L 50 80 L 50 120 L 10 120 L Z"></path>
</g>

clipboard.png

相对位置

简单来说,用小写字母(m, l)时,后面的坐标x,y可以理解为以当前位置为原点,向x和y轴方向移动相应距离、或画一条相应长度的线。上面两个正方形的例子可以用相对位置来画。

<g fill="none" stroke="black" stroke-width="5">
   <path d="m 10 10 l 40 0 l 0 40 l -40 0 z
        m 0 70 l 40 0 l 0 40 l -40 0 z"></path>
</g>

表示封闭的标识符z不区分大小写。另外,后面的标识符大小写都是表示绝对位置和相对位置。

缩写

基于节约是美德,带宽尽量省得原则,path属性也是能缩写就缩写。

垂线

Vertical lineTo首字母V(v),表示画一条垂线到指定y轴坐标(或相对位置)

<m 10 10 v 70> 与 <m 10 10 l 10 70> 等同

水平线

Horizontal lineTo首字母H(h),表示画一条水平线到指定x轴坐标(或相对位置)

<m 10 10 h 70> 与 <m 10 10 l 70 10> 等同

其他缩写

  • 字母和数字之间可以不用隔开。<path d="m10 10l40 0l0 40l-40 0z">

  • 如果path只用到直线,L(l)全都可以去掉。<path d="m10 10 40 0 0 40-40 0z">

  • 负数与数字(或字母)之间可以不用隔开。<path d="M12-24h15v25h-15z">

曲线

最BT的图形驾到~到这里才会知道几何知识还是挺重要的,各种切线,后面再说。

椭圆上的曲线

这算是一种最常规的曲线吧,就是以指定的x和y作为半径做一个椭圆,落在两个已知的点的中间的一段曲线。一般有四种可能。
盗张书中的图看下:

clipboard.png

两个已知的点分别为Arc start和Arc end,以指定x和y作为半径可做出两个经过这两点的椭圆,落在这两点中间的曲线就是黑色实线的四种情况,按顺逆时针方向分有两种,按两点和圆心之间形成的夹角是否超过180度分有两种,组合就是四种。当然还能旋转,如图f

Arc首字母A(a),表示画一条曲线,其后的前两个参数分别表示椭圆的x轴半径和y轴半径;第三个参数表示x-axis-rotation,即以x轴为基准旋转多少角度;第四个参数表示large-arc-flag,0表示夹角小于180度,1表示大于180度;第五个参数表示sweep-flag,0表示逆时针(counterclockwise),1表示顺时针(clockwise);最后两个参数表示第二个点坐标(Arc end)。

<g fill="none">
    <path d="M 125,75 A100,50 0 0,0 225,125" stroke="red"/> <!-- b -->
    <path d="M 125,75 A100,50 0 0,1 225,125" stroke="blue"/> <!-- c -->
    <path d="M 125,75 A100,50 0 1,0 225,125" stroke="black"/> <!-- d -->
    <path d="M 125,75 A100,50 0 1,1 225,125" stroke="black" stroke-dasharray="5 5"/> <!-- e -->
</g>

clipboard.png

两点需要注意的地方:

  1. Arc start和Arc end两个点如果重合,svg将画不出任何曲线。因为经过一个点的曲线有无数条。

  2. 如果Arc start和Arc end两个点之间距离太远导致已给定半径的任意椭圆都无法将两个点相连时,svg视图将会等比放大椭圆半径,直到有椭圆正好能够将两点相连。可以试试<path d="M 125,75 A100,50 0 0,0 500,75" stroke="red"/>,第二个点的x坐标变大,曲线也会跟着变长。

贝塞尔曲线

path里最牛X以及最容易让人懵逼和却步的曲线,分为二次贝塞尔曲线(quadratic Bézier curve)和三次贝塞尔曲线(cubic Bézier curve)。听名字就吓退了有木有啊!

好在svg已经做掉了核心算法的事情,暴露给使用者的仅仅就是几个坐标。也就是说,使用者只需要知道这几个坐标是干什么的,怎么用就行了,所以可以稍微不用太恐惧的看下去。具体这几个坐标是通过什么公式来精确控制曲线变化的,实在不知道也没问题。书里也根本没做详细解释,这是数学范畴(不懂的就恨当前数学没认真学吧)。

二次贝塞尔曲线

通过三个点控制曲线变化,开始和结束点,加上一个外部控制点(control point)。
曲线弯曲的情况如图
clipboard.png

想象一下,假设有两个点,分别从A和C点出发,沿着AC和CB两条线向C点和B点移动,并且以相同的时间t到达C点和B点,也就是说从A出发的点移动速度为AC/t,从C出发的点移动速度为CB/t。而两点之间的连线始终与曲线相切,这一条曲线便是三个点形成的二次贝塞尔曲线。

用Quadratic Bézier Curve首字母Q(q),表示二次贝塞尔曲线。前两个参数为控制点(control point)的坐标,后两个参数为曲线终点坐标。

就用上图的代码为例

<g fill="none">
    <path d="M 40 40L160 160L280 40" stroke="grey"></path>//AC、CB之间连线
    <line x1="100" y1="100" x2="220" y2="100" stroke="grey"></line>//切线
    <path d="m 40 40Q160 160 280 40" stroke="black"></path>//二次贝塞尔曲线
    <circle cx="40" cy="40" r="5" fill="black"></circle>//点A
    <circle cx="280" cy="40" r="5" fill="black"></circle>//点B
    <circle cx="160" cy="160" r="5" fill="black"></circle>//点C
    <text x="35" y="30" stroke="black">A</text>
    <text x="275" y="30" stroke="black">B</text>
    <text x="155" y="185" stroke="black">C</text>
</g>

另外,可以连续使用Q来画连续的二次贝塞尔曲线

<path d="m 40 40Q160 160 280 40Q 280 70 380 50" stroke="black"></path>

这样的曲线连接处会很生硬
clipboard.png
有个办法能让曲线很自然的链接到下一个曲线终点,那就是用字母T(t)
在第一个Q结束以后直接用T(t)加上终点坐标,可以让svg画出一条平滑优美的曲线。

<path d="m 40 40Q160 160 280 40T380 50" stroke="black"></path>

clipboard.png

实际上,svg会自动给后半段二次贝塞尔曲线加上一个控制点,使得这条连续曲线变得平滑。这个控制点坐标计算公式为:

x2 = x + (x - x1) = 2 * x - x1
y2 = y + (y - y1) = 2 * y - y1

(x2,y2)为自动生成的控制点坐标,(x,y)为前一段贝塞尔曲线的终点坐标,(x1,y1)为前一段曲线的控制点坐标。所以通过计算可以得出<path d="m 40 40Q160 160 280 40Q400-80 380 50" stroke="black"></path>和前面用T生成的曲线是一致的。不过显然用T方便得多。

三次贝塞尔曲线

通过四个点控制曲线变化,开始和结束点,加上两个外部控制点。

用Cubic Bézier Curve首字母C(c),表示三次贝塞尔曲线。前四个参数分别为两个控制点的坐标,后两个参数为曲线终点坐标。
感觉曲线的原理要说清楚有点力不从心了,直接上图吧。感觉其实和二次贝塞尔很相似,就是多了个控制点。
clipboard.png

三次贝塞尔可以生成许多有趣的曲线

clipboard.png

多个三次贝塞尔曲线的平滑链接,和二次一样,也有个特定字母S(s),后面接两组坐标,第一组表示第二个控制点坐标,第二组表示终点坐标,而由svg自动计算其第一组控制点的坐标。控制点计算公式与二次贝塞尔一样,只是(x1,y1)表示前一段曲线的第二个控制点坐标。


梦梦她爹
1.8k 声望122 粉丝