在svg中,所有的图形都可以用path来模拟实现,在MDN可以找到一篇入门教程。
绘制贝塞尔曲线的要点
从三次贝塞尔曲线入手,三次贝塞尔曲线需要一个坐标点和两个控制点,具体体现在标签path中就是d属性
C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)
其中x y是最终的终点,x1 y1,x2 y2是两个控制点的坐标。这样写有点抽象,我们来举个例子
这是一段贝塞尔曲线,它是由path标签产生,d属性为
M 162.5 338.5 C 165.5 230.5 301.5 223.5 290.5 292.5
它的起始点坐标为(162.5, 338.5),M表示的是move to,之后的C字母表示curve,也就是一段三次贝塞尔曲线。可以看成
C 控制点1(x, y) 控制点2(x, y) 终点(x, y)
绘制这一段贝塞尔曲线,使用path标签只需要知道坐标点和对应控制点的位置即可
数据结构
实际上这里的数据结构我们只需要存储坐标点和控制点,以及一些公共的属性
function path{
nodes: Node[],
strokeWidth: number,
stroke: string,
fill: string
}
function node {
anchorPoint: {
x,
y //锚点的坐标
},
ctrPoint: {
x,
y //控制点的坐标
}
}
一段path由多个锚点构成,故在path结构中添加了一个锚点的数组,用以存储点的坐标
渲染path
这里的工作有些像react的vdom,使用JavaScript来描述path的属性和各个点的位置,通过计算获取到d属性(一个path的字符串),交给浏览器去进行渲染,而不是直接操作DOM,维护和修改更加方便
const d = nodes.reduce((pre, cur, index) => {
if (index === 0) {
pre += `M ${cur.anchorPoint.x} ${cur.anchorPoint.y} C ${cur.ctrPoint.x} ${cur.ctrPoint.y}`
} else {
pre += `${cur.ctrPoint.x} ${cur.ctrPoint.y} C ${cur.anchorPoint.x} ${cur.anchorPoint.y}`
}
return pre
}, "")
需要将各个点的坐标组合成浏览器可以识别的D属性,之后交给浏览器去做渲染的工作
多段贝塞尔曲线
在实际中,很少会出现只有一段贝塞尔曲线的情况,例如在photoShop中,钢笔工具可以绘制多段贝塞尔曲线,幸运的是,path标签为我们提供了这样功能。
M 222.5 485.5 C 222.5 485.5 350.5 561.5 359.5 453.5 C 368.5 345.5 445.5 431.5 445.5 431.5
实际上只要不断添加C命令,就可以完成多段贝塞尔曲线的绘制,需要注意的是,两个C之间的交界点,锚点坐标相同而控制点坐标关于锚点中心对称
那么需要修改一下node的数据结构,添加第二控制点
function node {
anchorPoint: {
x,
y //锚点的坐标
},
ctrPoint: {
x,
y //控制点的坐标
}
ctr2Point: {
x,
y
}
}
同样修改一下D的计算方法
const d = nodes.reduce((pre, cur, index) => {
if (index === 0) {
pre += `M ${cur.anchorPoint.x} ${cur.anchorPoint.y} C ${cur.ctrPoint.x} ${cur.ctrPoint.y}` // 起始节点
} else if (index + 1 < nodes.length - 1) {
pre += `${nodes[i].ctrPosX} ${nodes[i].ctrPosY} ${nodes[i].posX} ${nodes[i].posY} C ${nodes[i].ctr2PosX} ${nodes[i].ctr2PosY} ` //中间节点
}
else {
pre += `${cur.ctrPoint.x} ${cur.ctrPoint.y} C ${cur.anchorPoint.x} ${cur.anchorPoint.y}` //末尾节点
}
return pre
}, "")
这样就完成了多段贝塞尔曲线的绘制