阅读 420

小白都能看懂的两点连线曲线算法

有个需求要实现两点间多条连线,效果如下:

这里的曲线可以直接通过二阶贝塞尔曲线来实现,曲线的控制点在两点连线的垂直线上,从直觉上我第一个想到的解法是要用到三角函数,于是就开始了以下推理过程

基础知识

弧度 = 角度 * Math.PI / 180
角度 = 弧度 * 180 / Math.PI

Math.atan2(y, x): 返回从 点 (x,y) 到 x轴 的弧度
Math.cos(弧度): 直角三角形中,相邻直角边/斜边 的值
Math.sin(弧度): 直角三角形中,相对直角边/斜边 的值

二阶贝塞尔曲线:由两个数据点(A 和 B),一个控制点(C)来描述曲线状态
复制代码

画图

推理逻辑

目标:假定控制点 CA B 连线中点的距离是固定的 10, 计算出红色直角三角形的两个直角边的长度

  • 通过 Math.atan2(y, x) 计算 A(x0, y0) 点到 B(x1, y1) 点的连线与 X 轴的弧度,这里需要用 y1-y0 得到 y 值,x1-x0 得到 x 值,它的作用如图上的蓝色线,可以理解为把 x y 轴平移到 A 点,通过计算得到了 角1 的弧度 h
  • 通过三角形原理可以推断出 角1 === 角2
  • 计算 角2 的相对直角边长度:L1 = Math.sin(h) * 10
  • 计算 角2 的相邻直角边长度:L2 = Math.cos(h) * 10
  • A B 连线中点坐标的计算方式 mx = (x0+x1)/2 my = (y0+y1)/2
  • 控制点 C 的坐标最终为 cx = mx + L1 cy = mx - L2

最终算法

function getXY(a, b, distance) {
    const y = b.y - a.y;
    const x = b.x - a.x;
    const radian = Math.atan2(y, x);
    const L1 = Math.sin(radian) * distance;
    const L2 = Math.cos(radian) * distance;
    const mx = (a.x + b.x)/2;
    const my = (a.y + b.y)/2;
    const cx = mx + L1;
    const cy = mx - L2;
    return { x: cx, y: cy };
}

const A = { x: 10, y: 30 };
const B = { x: 300, y: 200 };
const C = getXY(A, B, 10);
复制代码

Echarts算法

按自己的想法写完之后,我在 Echarts 的源码中中看到了这种曲线的另外一种超简单的算法

var x2 = (x0 + x1) / 2 - (y0 - y1) * 系数;
var y2 = (y0 + y1) / 2 - (x1 - x0) * 系数;
复制代码

简单的分析了一下,还是通过三角形的方法,可以判断 C 的坐标可以和 A B 点坐标差值产生关联,在不同的系数下可以得到所有中垂线上的点坐标

以上是从结果推算出来的结论,但它背后对应的公式原理,还不是很明白,希望能有掘友提供一些思路

参考资料


I'm Gafish 原创文章,首发于 我的博客,内容如有错误,还望指正,谢谢您的阅读。