如何获取SVG二次贝塞尔曲线峰值点坐标?有无替代方案?
解决两点间带弧度曲线及峰值点定位问题
一、二次贝塞尔曲线的峰值点计算
二次贝塞尔曲线本质是抛物线,其**极值点(即你所说的峰值点)**可通过参数方程求导推导得出。假设起点为 P0(x0,y0),控制点为 P1(x1,y1),终点为 P2(x2,y2),曲线的参数方程为:
x(t) = (1-t)²x0 + 2t(1-t)x1 + t²x2 y(t) = (1-t)²y0 + 2t(1-t)y1 + t²y2
若要找视觉上的峰值/谷值(y方向极值点),对y(t)求导并令导数为0,解得参数t0:
t0 = (y0 - y1) / (y0 - 2y1 + y2)
将t0代入参数方程,即可得到峰值点坐标(peakX, peakY),以下是动态计算的代码示例:
// 示例参数 const start = {x:20, y:50}; const control = {x:50, y:10}; const end = {x:100, y:80}; // 计算极值点参数t0 const denominator = start.y - 2 * control.y + end.y; const t0 = (start.y - control.y) / denominator; // 计算峰值点坐标 const peakX = Math.pow(1-t0,2)*start.x + 2*t0*(1-t0)*control.x + Math.pow(t0,2)*end.x; const peakY = Math.pow(1-t0,2)*start.y + 2*t0*(1-t0)*control.y + Math.pow(t0,2)*end.y; // 创建并添加circle元素 const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); circle.setAttribute("cx", peakX); circle.setAttribute("cy", peakY); circle.setAttribute("r", 3); circle.setAttribute("fill", "red"); document.querySelector("svg").appendChild(circle);
若需x方向的极值点,将上述公式中的y替换为x即可。
二、更适合轻微弧度的替代方案:SVG圆弧
如果仅需两点间的均匀轻微弧度,SVG的圆弧命令(A)比二次贝塞尔更合适,优势在于:
- 圆弧是圆的一部分,弧度均匀自然,弯曲程度易控制
- 峰值点(圆弧上离弦最远的点)的计算更直观
圆弧绘制语法
圆弧的路径命令格式为:
M x0 y0 A rx ry x-axis-rotation large-arc-flag sweep-flag x1 y1
rx/ry:圆弧的x/y半径(轻微弧度建议设为远大于两点间距的值)large-arc-flag:0表示小弧,1表示大弧sweep-flag:0表示逆时针绘制,1表示顺时针绘制
示例(轻微上凸圆弧):
<path d="M20 50 A2000 2000 0 0 1 100 80" fill="none" stroke="black"/>
圆弧峰值点计算
- 计算两点中点
(mx, my):mx = (x0 + x1)/2 my = (y0 + y1)/2 - 计算弦的方向向量
(dx, dy):dx = x1 - x0 dy = y1 - y0 - 计算垂直于弦的单位向量(上凸取
(-dy, dx),下凸取(dy, -dx)):length = sqrt(dx² + dy²) unitX = -dy / length unitY = dx / length - 计算峰值点到中点的距离
h(当半径r远大于弦长时,可近似h ≈ (dx² + dy²)/(8r)):h = sqrt(r² - (length/2)²) - 最终峰值点坐标:
peakX = mx + unitX * h peakY = my + unitY * h
三、方案选择建议
- 若需要灵活的非对称曲线形状,选择二次贝塞尔曲线,需处理极值点的参数计算
- 若仅需均匀的轻微弧度,优先选择SVG圆弧,计算更简单,曲线更自然
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

