SVG S命令贝塞尔曲线转数学公式后路径不匹配问题排查
问题描述
我有一段SVG路径:M 0, 100 S 27, 2, 200 0,尝试用贝塞尔方程将其转换为数学公式绘制线条,但发现SVG绘制的蓝色线条和公式计算出的红色线条不匹配。是不是我在提取控制点的时候理解错了?
原始代码
let c = document.getElementById('c') let ctx = c.getContext('2d') ctx.strokeStyle = 'blue' ctx.stroke(new Path2D(`M 0, 100 S 27, 2, 200 0`)) // bezier control points, derived from SVG path let x1 = 0, y1 = 100, x2 = 27, y2 = 2, x3 = 200, y3 = 0 ctx.strokeStyle = 'red' ctx.beginPath() ctx.moveTo(x1, y1) for(let t=0; t<=1; t+=0.0001) { ctx.lineTo( (1-t)**2 * x1 + 2*(1-t) * t * x2 + t**2 * x3, (1-t)**2 * y1 + 2*(1-t) * t * y2 + t**2 * y3 ) } ctx.stroke()
<canvas id="c" width="200" height="100"></canvas>
问题原因与解决方法
你错把SVG的S命令当成了二次贝塞尔曲线,但实际上S是光滑三次贝塞尔曲线的简写,对应的是三次贝塞尔公式,不是二次。
SVG S命令的规则
S x2,y2 x,y 表示从当前点绘制一条光滑三次贝塞尔曲线到终点(x,y):
- 如果前一个路径段是贝塞尔曲线(C/S/Q/T),则第一个控制点是前一段第二个控制点关于当前点的对称点;
- 如果前一个路径段不是贝塞尔曲线(比如这里的
M命令),则第一个控制点等于当前点(起点); - 第二个控制点是你指定的
(x2,y2),终点是(x,y)。
所以你的S 27,2 200,0对应的完整三次贝塞尔控制点是:
- P0(起点):(0, 100)
- P1(第一个控制点):(0, 100)(因为前面是M命令,无前置贝塞尔控制点)
- P2(第二个控制点):(27, 2)
- P3(终点):(200, 0)
修正后的代码
let c = document.getElementById('c') let ctx = c.getContext('2d') ctx.strokeStyle = 'blue' ctx.stroke(new Path2D(`M 0, 100 S 27, 2, 200 0`)) // 正确解析SVG S命令的三次贝塞尔控制点 let x0 = 0, y0 = 100, x1 = 0, y1 = 100, // 自动生成的第一个控制点 x2 = 27, y2 = 2, x3 = 200, y3 = 0 ctx.strokeStyle = 'red' ctx.beginPath() ctx.moveTo(x0, y0) for(let t=0; t<=1; t+=0.0001) { ctx.lineTo( (1-t)**3 * x0 + 3*(1-t)**2 * t * x1 + 3*(1-t)*t**2 * x2 + t**3 * x3, (1-t)**3 * y0 + 3*(1-t)**2 * t * y1 + 3*(1-t)*t**2 * y2 + t**3 * y3 ) } ctx.stroke()
<canvas id="c" width="200" height="100"></canvas>
修改后,红色线条会和蓝色线条完全重合。
内容的提问来源于stack exchange,提问作者Andrew Parks
相关产品推荐
相关产品推荐

