WebGL Tutorial
and more

Paths

撰写时间:2025-04-20

修订时间:2024-04-28

path只有一个d属性,其值中又含有各个非常灵活的命令,可绘制出丰富多彩的图形。

绘制直线

svg { border: 1px solid #444; }

M意为move,用以移动当前光标位置。L命令意为line to,指定一个点的位置后,将在当前光标位置与该点绘制一条直线。Z命令用以闭合图形。

因为可能需要多次调用L命令,因此多条连续的L命令可合并为1L命令,其后跟着一系列的点的坐标。

svg { border: 1px solid #444; }

上面,原来的L 100 400 L 600 400合并为L 100 400, 600 400

多数命令,如上面的M命令与L命令,有大小写之分,大写的命令使用绝对坐标值,小写的命令使用相对坐标值。

svg { border: 1px solid #444; }

使用小写命令l,用以指定偏移值,可清楚地看到,三角形的高为300px,底边为500px

L命令后面的数据必须至少两个以上的分别代表(x, y)坐标轴的数值,不够简练。我们可以使用Hh命令来绘制水平直线,使用Vv命令来绘制垂直直线。这两个命令,因为已经明确了方向,因此只需跟有一个数值就行了。

svg { border: 1px solid #444; }

可见,使用小写的hv命令,可让我们十分便捷地完成绘图任务。它具有以下优点:

  1. 直观
  2. 便于添加众多顶点
  3. 方便修改

绘制圆弧

由于需要以声明式的方式来绘制一个相对较为复杂的圆弧,SVG中绘制圆弧的算法比较独特。

arc

上图中,我们需要从点Arc start绘制一条圆弧至点Arc end

可以看出,这两个点为两个形状相同的椭圆的交点,或者说,这两个点来自两个椭圆。而若给定椭圆的长半径与短半径,从点Arc start绘制一条圆弧至点Arc end,共有4种绘制方法,根据这4种方法所绘制出来的圆弧在图中均已用红色标出。

4种结果依赖于需要绘制长圆弧还是短圆弧,以及是按逆时针还是按顺时针的方向来绘制。

假设我们需要绘制短圆弧。则图中第1行的第2列及第3列均为符合条件的短圆弧。第2列为按逆时针方向所绘制的短圆弧,它取自右上角这个椭圆的一小段圆弧。第3列为按顺时针方向所绘制的短圆弧,它取自左下角这个椭圆的一小段圆弧。

在这里,由于起点与终点已经确定下来,而从起点按逆时针方向绘制圆弧至终点,以及从起点按顺时针方向绘制圆弧至终点,将导致绘制出完全不同的两条圆弧。

现在,假设我们需要绘制长圆弧。则图中第2行的第2列及第3列均为符合条件的长圆弧。第2列为按逆时针方向所绘制的长圆弧,它取自左下角这个椭圆的一大段圆弧。第3列为按顺时针方向所绘制的长圆弧,它取自右上角这个椭圆的一大段圆弧。

换句话说,给定圆弧的起点与终点、长半径与短半径,这段圆弧可来自两个椭圆的相应部位。随着我们再接着确定长短圆弧及绘制方向的具体需求,这段圆弧也就随之确定下来。

我们注意到,尽管圆弧来自两个椭圆,但我们无需理会这两个椭圆如何绘制、它们的圆心各在哪里,以及所绘制的圆弧如何取自哪一个椭圆的哪一部位。所有这些,SVG的内部算法会自动帮我们确定。

可见,确定了圆弧的起点与终点,长半径与短半径,长弧或短弧,顺时针还是逆时针这几种关键因素后,这条圆弧也随之确定下来了。这就是SVG中使用A命令或a命令绘制圆弧的参数的由来,其原型如下:

voidA | a
  • floatrx, ry
  • numberx-axis-rotation
  • booleanlarge-arc-flag
  • booleansweep-flag
  • floatx, y

Aa命令从当前点的位置,绘制一条长短半径分别为rxry的圆弧至(x, y)。参数large-arc-flag指定是否需要绘制长圆弧,参数sweep-flag指定是按顺时针还是按逆时针的方向来绘制,这两个参数默认值为0,表示默认按逆时针方向绘制短弧。

参数x-axis-rotation是椭圆的旋转值,以角度为单位。

下面是绘制圆弧的代码:

svg { border: 1px solid #444; }

上面的代码,先从画布的中心(450, 300)绘制一条直线到(600, 300),则当前位置位于(600, 300)A命令从该位置绘制了一条圆弧至(450, 150),圆弧的长短半径各为200150。图中起点为红色,终点为绿色。

分别将代码中的large-arc-flag参数及sweep-flag参数的值改为1,尝试不同的组合,即可看到不同的效果。检查运行结果是否与上面图片中的效果一致。

绘制曲线

发明了Bézier曲线绘制方法的科学家真是太给力了。对于任意的一条线段,仅额外加入2个、甚至仅加入1个控制点,直线立即转变为千变万化的、姿态曼妙的弧线。计算机绘图从未如此轻松、如此优雅。

Cubic Bézier曲线

C, c命令

C命令是一个三阶的贝塞尔曲线,使用两个端点及两个控制点来绘制曲线。

svg { border: 1px solid #444; width: 100vw; height: 200px; & path { fill: none; stroke: white; } }

图像虽然绘制出来了,但很难看出4个端点与所生成图像的内在关系。下面通过拖动节点的方式来交互式地生成图像。

let selectedElement = null; let dataWrapper = { ptStart: {x:130, y:188}, cp1: {x:168, y:44}, cp2: {x:308, y:107}, ptEnd: {x:209, y:158} }; init(); function init() { let svg = document.querySelector('svg'); svg.style.cursor = 'default'; svg.addEventListener('mousemove', onSVGMouseMove); svg.addEventListener('mouseup', onSVGMouseUp); let circles = document.querySelectorAll('svg > circle'); circles.forEach(circle => { circle.addEventListener('mousedown', onCircleMouseDown); }); updateView(); } function updateView() { let entries = Object.entries(dataWrapper); for (let [id, point] of entries) { let element = document.querySelector(`#${id}`); element.setAttributeNS(null, 'cx', point.x); element.setAttributeNS(null, 'cy', point.y); } let path = document.querySelector(`#bezier-path`); const {ptStart, cp1, cp2, ptEnd} = dataWrapper; path.setAttributeNS(null, 'd', `M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y}`); let line1 = document.querySelector(`#line1`); line1.setAttributeNS(null, 'x1', ptStart.x); line1.setAttributeNS(null, 'y1', ptStart.y); line1.setAttributeNS(null, 'x2', cp1.x); line1.setAttributeNS(null, 'y2', cp1.y); let line2 = document.querySelector(`#line2`); line2.setAttributeNS(null, 'x1', ptEnd.x); line2.setAttributeNS(null, 'y1', ptEnd.y); line2.setAttributeNS(null, 'x2', cp2.x); line2.setAttributeNS(null, 'y2', cp2.y); let output = document.querySelector('#output'); output.innerHTML = `d = "M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y}"`; } function onSVGMouseMove(evt) { if (!selectedElement) { return; } dataWrapper[selectedElement.id].x = evt.offsetX; dataWrapper[selectedElement.id].y = evt.offsetY; updateView(); } function onSVGMouseUp(evt) { selectedElement = null; } function onCircleMouseDown(evt) { selectedElement = evt.target; }

svg { display: block; width: 100vw; height: 250px; border: 1px solid #444; & circle { fill: teal; stroke: #CCC; } #cp1, #cp2 { fill: #444; stroke: gray; } & path { fill: none; stroke: white; } & line { stroke: #FFF3; } }

拖动4个节点,曲线随之而变化,下面的文本则输出其d属性值。

代码使用了MVC模式,dataWrapper为数据模型,修改该模型的值,可改变曲线的初始状态。

可以看出,由于SVG内置支持其各个元素的事件响应,因此在SVG中编写drag and drop事件的代码非常轻松。

利用这个小工具,拖出我们想要的曲线后,复制其d属性值,再粘帖进其他的SVG代码,可方便地进行后续编辑。

这个功能可是PhotoShop, C4D等大腕软件的镇店之宝,而我们却仅用不到百行的代码就轻松地实现了此超酷的功能。

S, s命令

S命令属于一个连续绘制Cubic Bézier曲线的命令,在调用它之前,通常已有一个绘制Cubic Bézier曲线的命令。

svg { border: 1px solid #444; width: 100vw; height: 250px; & path { fill: none; stroke: white; } }

上面代码,有两个绘制Cubic Bézier曲线的命令,第一个是C命令,第二个是S命令。

S命令的参数较简单,只有一个终止端点的控制节点(303, 216),以及一个终止端点(312, 120)

实际上,S命令照样使用两个端点及两个控制点,但起始端点取自于当前点(204, 119)(也即S命令之前的C命令中的终止端点),起始端点的控制节点取自于上条曲线的第二个控制节点(260, 47)相对于当前点(204, 119)的镜像。

理解起来很费劲,但在交互式的图形应用中,这些概念立即变得清晰起来。

let selectedElement = null; let dataWrapper = { ptStart: {x:106, y:118}, cp1: {x:39, y:68}, cp2: {x:260, y:47}, ptEnd: {x:204, y:119}, s_cp1: {x: 0, y: 0}, s_cp2: {x: 303, y:216}, s_ptEnd: {x: 312, y:120} }; init(); function init() { let svg = document.querySelector('svg'); svg.style.cursor = 'default'; svg.addEventListener('mousemove', onSVGMouseMove); svg.addEventListener('mouseup', onSVGMouseUp); let circles = document.querySelectorAll('svg > circle'); circles.forEach(circle => { circle.addEventListener('mousedown', onCircleMouseDown); }); updateView(); } function updateView() { let offsetX = -(dataWrapper.cp2.x - dataWrapper.ptEnd.x); let offsetY = -(dataWrapper.cp2.y - dataWrapper.ptEnd.y); dataWrapper.s_cp1 = {x: dataWrapper.ptEnd.x + offsetX, y: dataWrapper.ptEnd.y + offsetY}; let entries = Object.entries(dataWrapper); for (let [id, point] of entries) { let element = document.querySelector(`#${id}`); element.setAttributeNS(null, 'cx', point.x); element.setAttributeNS(null, 'cy', point.y); } let path = document.querySelector(`#bezier-path`); const {ptStart, cp1, cp2, ptEnd, s_cp1, s_cp2, s_ptEnd} = dataWrapper; path.setAttributeNS(null, 'd', `M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y} S ${s_cp2.x},${s_cp2.y} ${s_ptEnd.x},${s_ptEnd.y}`); let line1 = document.querySelector(`#line1`); line1.setAttributeNS(null, 'x1', ptStart.x); line1.setAttributeNS(null, 'y1', ptStart.y); line1.setAttributeNS(null, 'x2', cp1.x); line1.setAttributeNS(null, 'y2', cp1.y); let line2 = document.querySelector(`#line2`); line2.setAttributeNS(null, 'x1', ptEnd.x); line2.setAttributeNS(null, 'y1', ptEnd.y); line2.setAttributeNS(null, 'x2', cp2.x); line2.setAttributeNS(null, 'y2', cp2.y); let line3 = document.querySelector(`#line3`); line3.setAttributeNS(null, 'x1', ptEnd.x); line3.setAttributeNS(null, 'y1', ptEnd.y); line3.setAttributeNS(null, 'x2', s_cp1.x); line3.setAttributeNS(null, 'y2', s_cp1.y); let line4 = document.querySelector(`#line4`); line4.setAttributeNS(null, 'x1', s_ptEnd.x); line4.setAttributeNS(null, 'y1', s_ptEnd.y); line4.setAttributeNS(null, 'x2', s_cp2.x); line4.setAttributeNS(null, 'y2', s_cp2.y); let output = document.querySelector('#output'); output.innerHTML = `d = "M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y} S ${s_cp2.x},${s_cp2.y} ${s_ptEnd.x},${s_ptEnd.y}"`; } function onSVGMouseMove(evt) { if (!selectedElement) { return; } dataWrapper[selectedElement.id].x = evt.offsetX; dataWrapper[selectedElement.id].y = evt.offsetY; updateView(); } function onSVGMouseUp(evt) { selectedElement = null; } function onCircleMouseDown(evt) { selectedElement = evt.target; }

svg { display: block; width: 100vw; height: 250px; border: 1px solid #444; & circle { fill: teal; stroke: #CCC; } #cp2 { fill: hsl(230, 50%, 50%); stroke: gray; } #ptEnd { fill: orange; } #cp1, #s_cp2 { fill: #444; stroke: gray; } #s_cp1 { fill: #222; stroke: gray; } & path { fill: none; stroke: white; } & line { stroke: #FFF3; } }

橙色的点,既是第一条C命令的终止端点,也成为第二条S命令的起始端点。

而黑色虚框的圆点,是S命令的起始端点的控制节点,它是C命令的终止控制节点(蓝色)相对于C命令终止端点(橙色)的镜像。由于它是SVG引擎自动求出的,因此在交互界面中不能拖动。相反,拖动蓝色控制节点,则可看到黑色控制节点的即时反应。

没有平滑曲线时的区别

let selectedElement = null; let dataWrapper = { ptStart: {x:106, y:118}, cp1: {x:39, y:68}, cp2: {x:260, y:47}, ptEnd: {x:204, y:119}, s_cp1: {x: 215, y: 203}, s_cp2: {x: 303, y:216}, s_ptEnd: {x: 312, y:120} }; init(); function init() { let svg = document.querySelector('svg'); svg.style.cursor = 'default'; svg.addEventListener('mousemove', onSVGMouseMove); svg.addEventListener('mouseup', onSVGMouseUp); let circles = document.querySelectorAll('svg > circle'); circles.forEach(circle => { circle.addEventListener('mousedown', onCircleMouseDown); }); updateView(); } function updateView() { let entries = Object.entries(dataWrapper); for (let [id, point] of entries) { let element = document.querySelector(`#${id}`); element.setAttributeNS(null, 'cx', point.x); element.setAttributeNS(null, 'cy', point.y); } let path = document.querySelector(`#bezier-path`); const {ptStart, cp1, cp2, ptEnd, s_cp1, s_cp2, s_ptEnd} = dataWrapper; path.setAttributeNS(null, 'd', `M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y} C ${s_cp1.x},${s_cp1.y} ${s_cp2.x},${s_cp2.y} ${s_ptEnd.x},${s_ptEnd.y}`); let line1 = document.querySelector(`#line1`); line1.setAttributeNS(null, 'x1', ptStart.x); line1.setAttributeNS(null, 'y1', ptStart.y); line1.setAttributeNS(null, 'x2', cp1.x); line1.setAttributeNS(null, 'y2', cp1.y); let line2 = document.querySelector(`#line2`); line2.setAttributeNS(null, 'x1', ptEnd.x); line2.setAttributeNS(null, 'y1', ptEnd.y); line2.setAttributeNS(null, 'x2', cp2.x); line2.setAttributeNS(null, 'y2', cp2.y); let line3 = document.querySelector(`#line3`); line3.setAttributeNS(null, 'x1', ptEnd.x); line3.setAttributeNS(null, 'y1', ptEnd.y); line3.setAttributeNS(null, 'x2', s_cp1.x); line3.setAttributeNS(null, 'y2', s_cp1.y); let line4 = document.querySelector(`#line4`); line4.setAttributeNS(null, 'x1', s_ptEnd.x); line4.setAttributeNS(null, 'y1', s_ptEnd.y); line4.setAttributeNS(null, 'x2', s_cp2.x); line4.setAttributeNS(null, 'y2', s_cp2.y); let output = document.querySelector('#output'); output.innerHTML = `d = "M ${ptStart.x},${ptStart.y} C ${cp1.x},${cp1.y} ${cp2.x},${cp2.y} ${ptEnd.x},${ptEnd.y} C ${s_cp1.x},${s_cp1.y} ${s_cp2.x},${s_cp2.y} ${s_ptEnd.x},${s_ptEnd.y}"`; } function onSVGMouseMove(evt) { if (!selectedElement) { return; } dataWrapper[selectedElement.id].x = evt.offsetX; dataWrapper[selectedElement.id].y = evt.offsetY; updateView(); } function onSVGMouseUp(evt) { selectedElement = null; } function onCircleMouseDown(evt) { selectedElement = evt.target; }

svg { display: block; width: 100vw; height: 250px; border: 1px solid #444; & circle { fill: teal; stroke: #CCC; } #cp2 { fill: hsl(230, 50%, 50%); stroke: gray; } #ptEnd { fill: orange; } #cp1, #s_cp2 { fill: #444; stroke: gray; } #s_cp1 { fill: #222; stroke: gray; } & path { fill: none; stroke: white; } & line { stroke: #FFF3; } }

可以看出,当第2个节点不使用S命令,而使用C命令时,则可以独立地控制2个控制点;移动其中一个控制点,另一条曲线不受任何影响。

两个控制点与黄色端点分别组成了两条直线。当这两条直线的夹角为180°时,曲线的弧度过渡得最为平滑。因此使用S命令所绘制出来的曲线,也称为平滑曲线smooth curve)。

Quadratic Bézier曲线

Q命令

Q命令是一个二阶的贝塞尔曲线,使用两个端点及一个控制点来绘制曲线。

svg { border: 1px solid #444; height: 250px; & path { fill: none; stroke: white; } }

交互。

let selectedElement = null; let dataWrapper = { ptStart: { x: 109, y: 198 }, cp: { x: 138, y: 42 }, ptEnd: { x: 349, y: 100 } }; init(); function init() { let svg = document.querySelector('svg'); svg.style.cursor = 'default'; svg.addEventListener('mousemove', onSVGMouseMove); svg.addEventListener('mouseup', onSVGMouseUp); let circles = document.querySelectorAll('svg > circle'); circles.forEach(circle => { circle.addEventListener('mousedown', onCircleMouseDown); }); updateView(); } function updateView() { let entries = Object.entries(dataWrapper); for (let [id, point] of entries) { let element = document.querySelector(`#${id}`); element.setAttributeNS(null, 'cx', point.x); element.setAttributeNS(null, 'cy', point.y); } let path = document.querySelector(`#bezier-path`); const {ptStart, cp, ptEnd} = dataWrapper; path.setAttributeNS(null, 'd', `M ${ptStart.x},${ptStart.y} Q ${cp.x},${cp.y} ${ptEnd.x},${ptEnd.y}`); let line1 = document.querySelector(`#line1`); line1.setAttributeNS(null, 'x1', ptStart.x); line1.setAttributeNS(null, 'y1', ptStart.y); line1.setAttributeNS(null, 'x2', cp.x); line1.setAttributeNS(null, 'y2', cp.y); let line2 = document.querySelector(`#line2`); line2.setAttributeNS(null, 'x1', ptEnd.x); line2.setAttributeNS(null, 'y1', ptEnd.y); line2.setAttributeNS(null, 'x2', cp.x); line2.setAttributeNS(null, 'y2', cp.y); let output = document.querySelector('#output'); output.innerHTML = `d = "M ${ptStart.x},${ptStart.y} Q ${cp.x},${cp.y} ${ptEnd.x},${ptEnd.y}"`; } function onSVGMouseMove(evt) { if (!selectedElement) { return; } dataWrapper[selectedElement.id].x = evt.offsetX; dataWrapper[selectedElement.id].y = evt.offsetY; updateView(); } function onSVGMouseUp(evt) { selectedElement = null; } function onCircleMouseDown(evt) { selectedElement = evt.target; }

svg { display: block; width: 100vw; height: 250px; border: 1px solid #444; & circle { fill: teal; stroke: #CCC; } #cp { fill: #444; stroke: gray; } & path { fill: none; stroke: white; } & line { stroke: #FFF3; } }

T命令

svg { border: 1px solid #444; width: 100vw; height: 250px; & path { fill: none; stroke: white; } }

交互。

let selectedElement = null; let dataWrapper = { ptStart: {x:108, y:127}, cp: {x:130, y:27}, ptEnd: {x:183, y:120}, t_cp: {x: 0, y: 0}, t_ptEnd: {x: 394, y:75} }; init(); function init() { let svg = document.querySelector('svg'); svg.style.cursor = 'default'; svg.addEventListener('mousemove', onSVGMouseMove); svg.addEventListener('mouseup', onSVGMouseUp); let circles = document.querySelectorAll('svg > circle'); circles.forEach(circle => { circle.addEventListener('mousedown', onCircleMouseDown); }); updateView(); } function updateView() { let offsetX = -(dataWrapper.cp.x - dataWrapper.ptEnd.x); let offsetY = -(dataWrapper.cp.y - dataWrapper.ptEnd.y); dataWrapper.t_cp = {x: dataWrapper.ptEnd.x + offsetX, y: dataWrapper.ptEnd.y + offsetY}; let entries = Object.entries(dataWrapper); for (let [id, point] of entries) { let element = document.querySelector(`#${id}`); element.setAttributeNS(null, 'cx', point.x); element.setAttributeNS(null, 'cy', point.y); } let path = document.querySelector(`#bezier-path`); const {ptStart, cp, ptEnd, t_cp, t_ptEnd} = dataWrapper; path.setAttributeNS(null, 'd', `M ${ptStart.x},${ptStart.y} Q ${cp.x},${cp.y} ${ptEnd.x},${ptEnd.y} T ${t_ptEnd.x},${t_ptEnd.y}`); let line1 = document.querySelector(`#line1`); line1.setAttributeNS(null, 'x1', ptStart.x); line1.setAttributeNS(null, 'y1', ptStart.y); line1.setAttributeNS(null, 'x2', cp.x); line1.setAttributeNS(null, 'y2', cp.y); let line2 = document.querySelector(`#line2`); line2.setAttributeNS(null, 'x1', ptEnd.x); line2.setAttributeNS(null, 'y1', ptEnd.y); line2.setAttributeNS(null, 'x2', cp.x); line2.setAttributeNS(null, 'y2', cp.y); let line3 = document.querySelector(`#line3`); line3.setAttributeNS(null, 'x1', ptEnd.x); line3.setAttributeNS(null, 'y1', ptEnd.y); line3.setAttributeNS(null, 'x2', t_cp.x); line3.setAttributeNS(null, 'y2', t_cp.y); let line4 = document.querySelector(`#line4`); line4.setAttributeNS(null, 'x1', t_ptEnd.x); line4.setAttributeNS(null, 'y1', t_ptEnd.y); line4.setAttributeNS(null, 'x2', t_cp.x); line4.setAttributeNS(null, 'y2', t_cp.y); let output = document.querySelector('#output'); output.innerHTML = `d = "M ${ptStart.x},${ptStart.y} Q ${cp.x},${cp.y} ${ptEnd.x},${ptEnd.y} T ${t_ptEnd.x},${t_ptEnd.y}"`; } function onSVGMouseMove(evt) { if (!selectedElement) { return; } dataWrapper[selectedElement.id].x = evt.offsetX; dataWrapper[selectedElement.id].y = evt.offsetY; updateView(); } function onSVGMouseUp(evt) { selectedElement = null; } function onCircleMouseDown(evt) { selectedElement = evt.target; }

svg { display: block; width: 100vw; height: 250px; border: 1px solid #444; & circle { fill: teal; stroke: #CCC; } #cp { fill: hsl(230, 50%, 50%); stroke: gray; } #ptEnd { fill: orange; } #t_cp { fill: #222; stroke: gray; } & path { fill: none; stroke: white; } & line { stroke: #FFF3; } }

Path DOM

本节部分的APIs只适用于Safari,Chrome目前尚未支持。

CanvasPath2D不能直接操控子路径。而SVGSVGPathElement则提供了直接操控子路径的方法。

let path2D = new Path2D(); pc.log('%O', path2D); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); pc.log('%O', path);

path的类为SVGPathElement,有以下添加各种子路径的方法:

类别方法名称d属性中的命令作用
指定当前位置createSVGPathSegMovetoAbsM使用绝对值,将指定位置设置为当前位置
createSVGPathSegMovetoRelm使用相对值,将指定位置设置为当前位置
绘制直线createSVGPathSegLinetoAbsL使用绝对值,从当前位置绘制一条直线至指定位置
createSVGPathSegLinetoRell使用相对值,从当前位置绘制一条直线至指定位置
createSVGPathSegLinetoHorizontalAbsH使用绝对值,从当前位置绘制一条水平直线至指定位置
createSVGPathSegLinetoHorizontalRelh使用相对值,从当前位置绘制一条水平直线至指定位置
createSVGPathSegLinetoVerticalAbsV使用绝对值,从当前位置绘制一条竖直的直线至指定位置
createSVGPathSegLinetoVerticalRelv使用相对值,从当前位置绘制一条竖直的直线至指定位置
绘制圆弧createSVGPathSegArcAbsA使用绝对值,从当前位置绘制一条圆弧至指定位置
createSVGPathSegArcRela使用相对值,从当前位置绘制一条圆弧至指定位置
绘制Cubic Bézier曲线createSVGPathSegCurvetoCubicAbsC使用绝对值,从当前位置绘制一条Cubic Bézier曲线至指定位置
createSVGPathSegCurvetoCubicRelc使用相对值,从当前位置绘制一条Cubic Bézier曲线至指定位置
createSVGPathSegCurvetoCubicSmoothAbsS使用绝对值,从当前位置绘制一条平滑的Cubic Bézier曲线至指定位置
createSVGPathSegCurvetoCubicSmoothRels使用相对值,从当前位置绘制一条平滑的Cubic Bézier曲线至指定位置
绘制Quad Bézier曲线createSVGPathSegCurvetoQuadraticAbsQ使用绝对值,从当前位置绘制一条Quad Bézier曲线至指定位置
createSVGPathSegCurvetoQuadraticRelq使用相对值,从当前位置绘制一条Quad Bézier曲线至指定位置
createSVGPathSegCurvetoQuadraticSmoothAbsT使用绝对值,从当前位置绘制一条平滑的Quad Bézier曲线至指定位置
createSVGPathSegCurvetoQuadraticSmoothRelt使用相对值,从当前位置绘制一条平滑的Quad Bézier曲线至指定位置
关闭路径createSVGPathSegClosePathz, Z关闭路径

下面使用上述方法来构建一个正方形,然后,通过SVGPathElementpathSegList来检视每个子路径。

let pathEle = document.querySelector('svg > path'); function constructPath() { let pathSeg = pathEle.createSVGPathSegMovetoAbs(50, 50); pathEle.pathSegList.appendItem(pathSeg); pathSeg = pathEle.createSVGPathSegLinetoHorizontalRel(50); pathEle.pathSegList.appendItem(pathSeg); pathSeg = pathEle.createSVGPathSegLinetoVerticalRel(50); pathEle.pathSegList.appendItem(pathSeg); pathSeg = pathEle.createSVGPathSegLinetoHorizontalRel(-50); pathEle.pathSegList.appendItem(pathSeg); pathSeg = pathEle.createSVGPathSegClosePath(); pathEle.pathSegList.appendItem(pathSeg); } function showSubPaths() { let segList = pathEle.pathSegList; pc.log('%O', segList); for (let index = 0; index < segList.length; index++) { let pathSeg = segList.getItem(index); pc.log('%O', pathSeg); } } constructPath(); showSubPaths();
svg { border: 1px solid #444; width: 100vw; height: 150px; }

了解这些APIs的细节后,我们就可以通过代码来创建可动态交互的路径了。

参考资源

  1. SVG2 Paths
  2. Geometry Interfaces Module Level 1
  3. CSS Object Model (CSSOM)
  4. SVG2 Basic Data Types and Interfaces
  5. SVG2 The grammar for path data