LogicFlow 是專一於業務流程圖可視化的前端框架(如下簡稱 LF),在這以前咱們分別介紹了 LF 的總體設計和擴展機制,今天咱們來聊一聊流程圖中比較核心的一個概念 —— 邊(Edge)。html
首先,流程圖中的元素主要由節點和邊組成,邊在 LF 中承擔的角色是創建節點之間的關係。不一樣場景對圖的佈局和美觀度都有要求,目前, LF 中提供了直線、折線、曲線 3 種類型的線來知足不一樣的需求。以下圖所示。前端
此外,邊的基礎功能包括路徑繪製、箭頭、文案,爲了豐富操做還包括選中、調整等功能。本文將會介紹直線、折線、曲線的繪製,邊文案設置,選中區域擴大,選中狀態標識,位置調整,樣式調整等實現思路和功能設計。git
爲了方便理解,先對一些名詞和功能作下解釋。
繪製:繪製線的形狀。
選中區域擴大:通常邊設置的寬度較小( 1 - 2px),精確點擊進行選中的實際操做比較困難,將選中區域擴大以後,可使邊更容易被鼠標選中。
選中狀態標識:將選中的邊與其餘邊進行區分標識。
位置調整:邊的建立是經過錨點進行鏈接,自動計算其路徑,可是路徑會存在一些固定計算的弊端,能夠經過手動調整位置,達到視覺最優。
邊文案設置:邊上設置文案豐富信息表達。
樣式調整:系統提供了邊的默認樣式,例如:邊的顏色爲 #000000,寬度爲 2px,若是不一樣宿主系統的須要不一樣風格,LF 中也提供了樣式調整的方法。github
LF 中提了供直線、直角折線、光滑曲線 3 種類型的邊,本文將逐一介紹相關的實現思路。算法
兩點肯定一條直線,僅須要邊的起點和終點便可繪製出直線,在 LF 中使用 svg 的 進行繪製。前端框架
選中區域擴大是在直線上增長矩形來實現的,以與兩個端點相鄰距離爲 10 的點爲垂點,計算與垂點距離爲 5,垂直於直線兩邊的點,結果能夠獲得 4 個點組成一個矩形,因計算結果爲 4 點座標,使用了 path 標籤進行渲染。目前直線不支持起始位置的調整。微信
圖中紫色爲直線擴大的可點區域markdown
ps:爲何要設置 offset = 10,而不是 offset = 0,或者使用繪製同樣的圖形加大寬度 (stroke-width) 進行實現呢?考慮到後面會有邊起點、終點位置調整,以及擴展功能,這樣的方式更加靈活和可控。框架
兩點之間若是僅使用直線進行鏈接,當節點數量增多,位置關係複雜時,會出現大量連線和節點交叉和重合的現象,爲了更加清晰的表達節點之間的關係,LF 支持直角折線來鏈接兩個節點。 在 LF 中直角折線使用 svg 中的 polyline 標籤進行繪製,關鍵步驟是找出組成折線的點。考慮美觀性,策略爲邊儘可能不與節點的邊產生交叉和重合。 首先假定使用節點上錨點 Start —> 錨點 End 進行鏈接,如下面的圖爲例介紹,如何計算直角折線路徑的點。ide
第一步:計算以 Start 和 End 爲垂足,垂直於的 Start 和 End 所在邊框且距離爲100的點,以下圖所示,先計算出與 Start 所在節點邊框距離爲 100 的 SBbox,在 SBbox 上能夠找到垂直於的 Start,且與其所在節點邊框距離爲 100 的點,這個點即爲 Start 的下一個點,將其命名爲 StartNext。同理可得點 EndPre。本次計算獲得四個點,[Start, StartNext, EndPre, End],這些點爲路徑肯定通過的點。
第二步:將包含 StartNext 和 EndPre 連線的盒子命名爲 LBox,包含 SBbox 和 LBbox 的盒子命名爲 SLBbox,包含 EBbox 和 LBbox 的盒子命名爲 ELBbox,取 SLBbox 和 ELBbox 四個角上的點,即圖中藍色的點,這些點爲折線路徑中可能通過的點。這一步獲得的可能的點爲【藍色的點】
第三步:找出 StartNext 和 EndPre 的中點,下圖中綠點,找出中點X,Y 軸上直線與 LBbox、SLBbox、ELBbox 相交,且不在 SBbox 和 EBbox 中的點,即爲圖中紫色的點,這些點爲折線路徑中可能通過的點。這一步獲得的可能的點爲【紫色的點】
第四步:將前面三步獲得的點彙總,而後對相同座標的點進行去重,將會獲得以下圖中紅色的點,接下來就是求 StartNext 到 EndPre 的最優路徑。
第五步:採用 A*查找 結合 曼哈頓距離計算路徑,獲得如圖所示路徑。
第六步:過濾同一直線上的中間節點,獲得以下點,並鏈接成折線,至此折線路徑部分繪製完成。
以上介紹了當兩個節點之間存在必定距離,即 SBbox 與 EBbox 不存在重合時,路徑計算的方法,這也是大部分繪圖會涉及到的情景。當兩個節點距離較近時,將採用其餘簡單策略來進行直角折線的實現,本文不作詳細介紹。 折線點擊區域擴大的實現,是將折線分紅多個線段,每一個線段採用與直線一樣的方式。詳細介紹可參考直線部分。示例以下如:
圖中紫色爲折線擴大的可點區域
將折現分紅多個線段分別處理,同時也方便了折線的位置調整。目前LF中能夠對摺線中的各個線段,進行水平/垂直方向的移動調整。 LF中線段位置調整是根據移動位置,實時從新計算路徑的方式實現的。步驟以下:
第一步:根據移動座標,計算出當前線段兩個端點拖拽移動後的座標。
第二步:計算拖拽移動調整後,線段與節點外框的交點。
第三步:調整到對應外框的位置後,找到當前線段和圖形的準確交點,更新路徑。 如下圖爲矩形和圓形垂直向下調整爲示例,調整效果以下。
LF 也提供了曲線的方式來繪製邊。LF 是基於 svg 進行繪製的,svg 中 path 標籤自然支持對於貝塞爾曲線的繪製,爲了減小計算,LF 中的光滑曲線是基於貝塞爾曲線實現的,貝塞爾曲線能夠依據四個任意座標的點繪製出的一條光滑曲線,經過控制曲線上的四個點(起點、終點以及兩個相互分離的中間支點)來建立、編輯圖形。在LF中能夠經過移動兩個支點的位置來進行曲線形狀調整。
爲了繪製貝塞爾曲線,須要計算出控制曲線上的四個點,其中起點和終點是已知的,關鍵點是如何計算出 2 箇中間支點,爲了圖的美觀性,線與節點的邊框最大程度不產生重合,以及計算複雜度,實現步驟以下: 第一步:計算出節點邊框的相關座標
第二步:計算出節點距離節點邊框 offset = 100 的外框的相關座標,
第三步:判斷中心點與起點所在線段的方向(水平/垂直),在中心點與起點相同的方向上計算距離起點距離 offset = 100 對應的支點,這個支點就在第二步描述的節點外框上,以上圖示例,根據其位置,取節點外框中的點(座標爲:x: 最大X軸座標,y: 中心點Y座標)爲支點。同理能夠計算出終點對應的支點。此方法與查找折線路徑中 StartNext 和 EndPre 相同。 第四步:獲得兩個支點後,結合起點和終點,使用 path 標籤進行繪製。 曲線繪製效果以下,其中藍色圓形即爲其支點,能夠經過移動支點位置,調整線的形狀。
擴大貝塞爾曲線的選中區域,繪製相同位置的曲線,可是樣式屬性以下的貝塞爾曲線:
strokeWidth="10"
stroke="transparent"
fill="none"
複製代碼
擴大區域是一個寬度增長 10 的貝塞爾曲線。
圖中紫色爲貝塞爾曲線擴大的可點區域
流程圖中箭頭標明瞭流程節點的指向,在 LF 中直線、折線、曲線的箭頭使用統一方案實現,在 LF 中的箭頭本質是一個包含終點的三角形,其中終點是肯定的,須要計算另外 2 點組成三角形。
直線:起點到終點的向量
折線:折線中最後一個線段的向量
曲線:曲線中一共有 4 個點,取終點對應的支點到終點的向量
以與終點相鄰距離爲10的點爲垂點,計算垂直於向量與垂點距離爲5的兩點點,結果能夠獲得 3 個點組成三角形,即爲箭頭。以下如所示,箭頭大小能夠經過主題樣式設置 offset 和 verticalLength 來進行寬高調整。
目前在LF中邊僅支持單向箭頭,且箭頭樣式僅爲三角形,後續會繼續豐富箭頭的能力展示。
邊的選中是經過一個可以包含邊的全部點的矩形進行標識的。經過計算出矩形座標以及大小信息:x, y, width, height,而後進行渲染,這些選中標識與節點是在不一樣的 svg 圖層中進行繪製的,LF 是基於 MVVM (具體可參考滴滴開源 LogicFlow:專一流程可視化的前端框架),能夠很方便的經過數據驅動進行分層渲染,一樣節點的選中標識也是類似的分層方式實現,這樣能夠更加靈活的處理不一樣模式和條件下的渲染,同時下載圖片時也方便進行選中標識圖層的剔除,獲得更加純粹乾淨的圖。直線、折線、曲線因其路徑計算的差別,選中標識計算方法也有所不一樣。
直線的計算邏輯以下:
const x = (startPoint.x + endPoint.x) / 2;
const y = (startPoint.y + endPoint.y) / 2;
const width = Math.abs(startPoint.x - endPoint.x) + 10;
const height = Math.abs(startPoint.y - endPoint.y) + 10;
複製代碼
折線的計算邏輯以下:
// bbox: 包含折線上全部點的box
const { points } = polyline;
const pointsList = points2PointsList(points);
const bbox = getBBoxOfPoints(pointsList, 8);
const { x, y, width, height, } = bbox;
複製代碼
曲線的計算邏輯以下:
const { path } = bezier;
const pointsList = getBezierPoints(path);
const bbox = getBBoxOfPoints(pointsList, 8);
const { x, y, width, height, } = bbox;
複製代碼
邊上設置文案能夠豐富信息表達,在 LF 中能夠經過雙擊邊開啓文案編輯,文案默認位置以下:
固然文案位置也能夠手動進行拖動調整。
關於邊的樣式調整詳細內容,能夠查看官方文檔介紹-主題Theme。
相信經過本文你對 Edge 的實現有一個大概的認知了,其實在作 LogicFlow 的過程當中,咱們遇到了不少非純前端的問題,那就須要咱們去重拾幾何、算法這樣的知識,若是你也對這方面很是感興趣或者有研究,歡迎一塊兒交流。目前,LogicFlow 用戶羣的人數已經 200+,你們都在討論流程可視化/LowCode 相關實現,期待你的參與~
更多閱讀資料: