如何计算饼图分段圆角处二次贝塞尔曲线的控制点
带圆角缺口饼图的二次贝塞尔控制点计算方案
核心思路
之前用两点连线垂直方向确定控制点的方法会出现方向错误,原因是没有结合饼图中心的位置判断外侧/内侧方向。正确的做法是基于饼图中心到缺口两端点的向量角平分线来确定控制点的偏移方向,这样无论缺口在饼图的哪个位置,方向都不会出错。
具体步骤(JavaScript + PDFKit)
假设已知参数:饼图中心坐标(centerX, centerY)、饼图半径pieRadius、圆角大小cornerRadius、缺口两端点的角度startAngle/endAngle(或直接已知端点坐标P1(x1,y1)、P2(x2,y2))。
- 计算缺口端点坐标(如果已知角度):
const x1 = centerX + pieRadius * Math.cos(startAngle); const y1 = centerY + pieRadius * Math.sin(startAngle); const x2 = centerX + pieRadius * Math.cos(endAngle); const y2 = centerY + pieRadius * Math.sin(endAngle);
- 计算中心到端点的单位向量:
const vec1 = { x: x1 - centerX, y: y1 - centerY }; const vec2 = { x: x2 - centerX, y: y2 - centerY }; const len1 = Math.sqrt(vec1.x ** 2 + vec1.y ** 2); const len2 = Math.sqrt(vec2.x ** 2 + vec2.y ** 2); const unitVec1 = { x: vec1.x / len1, y: vec1.y / len1 }; const unitVec2 = { x: vec2.x / len2, y: vec2.y / len2 };
- 计算角平分线单位向量:
角平分线方向就是控制点需要偏移的方向(外侧圆角用正方向,内侧缺口圆角用反方向):
const bisectorVec = { x: unitVec1.x + unitVec2.x, y: unitVec1.y + unitVec2.y }; const bisectorLen = Math.sqrt(bisectorVec.x ** 2 + bisectorVec.y ** 2); const unitBisector = { x: bisectorVec.x / bisectorLen, y: bisectorVec.y / bisectorLen };
- 计算控制点坐标:
从端点中点出发,沿角平分线方向偏移cornerRadius距离:
const midX = (x1 + x2) / 2; const midY = (y1 + y2) / 2; // 外侧圆角用加法,内侧缺口圆角用减法 const ctrlX = midX + unitBisector.x * cornerRadius; const ctrlY = midY + unitBisector.y * cornerRadius;
- PDFKit绘制路径:
// 绘制带圆角缺口的饼图 doc.moveTo(x1, y1) .quadraticCurveTo(ctrlX, ctrlY, x2, y2) // 圆角缺口边 .lineTo(centerX, centerY) .closePath() .fill('orange');
关键说明
- 角平分线方向基于饼图中心计算,完全避免了方向错误问题,适配饼图任意位置的缺口/分段。
- 圆角大小可通过调整
cornerRadius参数直接控制,数值越大圆角越明显。 - 若需要内侧凹圆角(如吃豆人嘴巴的缺口),只需将控制点偏移方向取反(把加法改成减法)。
内容的提问来源于stack exchange,提问作者espenhogbakk
相关产品推荐
相关产品推荐

