有個需求要實現兩點間多條連線,效果以下:git
這裏的曲線能夠直接經過二階貝塞爾曲線來實現,曲線的控制點在兩點連線的垂直線上,從直覺上我第一個想到的解法是要用到三角函數,因而就開始瞭如下推理過程github
弧度 = 角度 * Math.PI / 180
角度 = 弧度 * 180 / Math.PI
Math.atan2(y, x): 返回從 點 (x,y) 到 x軸 的弧度
Math.cos(弧度): 直角三角形中,相鄰直角邊/斜邊 的值
Math.sin(弧度): 直角三角形中,相對直角邊/斜邊 的值
二階貝塞爾曲線:由兩個數據點(A 和 B),一個控制點(C)來描述曲線狀態
複製代碼
目標:假定控制點 C
到 A
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
的源碼中中看到了這種曲線的另一種超簡單的算法apache
var x2 = (x0 + x1) / 2 - (y0 - y1) * 係數;
var y2 = (y0 + y1) / 2 - (x1 - x0) * 係數;
複製代碼
簡單的分析了一下,仍是經過三角形的方法,能夠判斷 C
的座標能夠和 A
B
點座標差值產生關聯,在不一樣的係數下能夠獲得全部中垂線上的點座標bash
以上是從結果推算出來的結論,但它背後對應的公式原理,還不是很明白,但願能有掘友提供一些思路echarts
I'm Gafish 原創文章,首發於 個人博客,內容若有錯誤,還望指正,謝謝您的閱讀。svg