如何求取线段中点沿法线偏移指定距离的点坐标
Canvas线段法线偏移标签点最简实现
不需要解复杂的距离方程,用向量运算3步就能算出结果,代码实现没有复杂推导,鲁棒性也更高。
计算步骤
- 第一步:算线段中点和方向向量
设线段两端点为(x1,y1)、(x2,y2),中点坐标直接套中点公式:mx = (x1 + x2) / 2,my = (y1 + y2) / 2
沿线段方向的向量为:dx = x2 - x1,dy = y2 - y1 - 第二步:求单位法线向量
垂直于线段的法线方向不需要推导直线方程,直接把方向向量旋转90度即可,两个旋转方向分别对应线段两侧:- 顺时针旋转90度法线:
(dy, -dx) - 逆时针旋转90度法线:
(-dy, dx)
先算线段总长度做归一化:len = Math.hypot(dx, dy),如果len为0说明两端点重合,直接返回中点即可,避免除零错误。
把选好的法线向量除以线段长度,就得到长度为1的单位法线向量。
- 顺时针旋转90度法线:
- 第三步:按偏移距离算最终坐标
设定你需要的偏移距离offset(单位为像素,正数沿法线方向偏移,负数则反向偏移),最终标签锚点坐标为:xn = mx + 单位法线x分量 * offsetyn = my + 单位法线y分量 * offset
可直接复用的Canvas代码
/** * 计算线段中点法线偏移位置,用于放置标签 * @param {number} x1 线段起点x * @param {number} y1 线段起点y * @param {number} x2 线段终点x * @param {number} y2 线段终点y * @param {number} offset 偏移像素距离,默认10px * @param {boolean} flipSide 是否翻转到线段另一侧,默认false * @returns {{x: number, y: number}} 标签锚点坐标 */ function getLineLabelAnchor(x1, y1, x2, y2, offset = 10, flipSide = false) { const mx = (x1 + x2) / 2; const my = (y1 + y2) / 2; const dx = x2 - x1; const dy = y2 - y1; const lineLen = Math.hypot(dx, dy); // 两端点重合时直接返回中点 if (lineLen === 0) return {x: mx, y: my}; // 计算单位法线 let nx = -dy / lineLen; let ny = dx / lineLen; // 翻转到另一侧 if (flipSide) { nx = -nx; ny = -ny; } return { x: mx + nx * offset, y: my + ny * offset }; }
实用优化提示
实际绘制标签时可以参考这几个小技巧,显示效果更好:
- 偏移量可以多留2-3px冗余,不要刚好卡着文字不压线的距离,视觉上会更宽松
- 如果需要标签始终显示在线段上方(水平线段时标签在更靠上的一侧),可以判断法线向量的y分量正负,自动选择y值更小的一侧,不需要手动传翻转参数
- 绘制文字前先在同样位置画一个和文字轮廓匹配的半透明白色底,或者给文字加1-2px的白色描边,就算偶尔和其他图形重叠也能看清文字
内容的提问来源于stack exchange,提问作者Constantcraft
相关产品推荐
相关产品推荐

