在线流程图和思维导图开发技术详解(二)
一、項目概述
二、項目架構
三、幾何計算難點
四、鼠標事件處理
五、數據保存與導出
六、文本處理
二、項目架構
2.1 系統架構
項目系統架構如下圖所示:
UI元素即HTML DOM元素。項目打開時,畫面雖然是一片空白的,但實際上UI元素并不空白。一些可復用不需動態創建的元素可預先定義好。例如選擇框、對齊線等,出現的時候它們只是位置發生了改變,不需動態創建,可預先定義好,節省開銷。
UI元素使用ID標注(Vue里使用ref),然后注冊到UI管理器中。后面對UI管理器中相應的對象進行修改時,UI元素就會發生改變。
對UI元素的所有操作,例如動一下鼠標、點一下按鈕、按一下鍵盤,所有的這些事件都會交給事件處理器處理。事件處理器包括鼠標事件、基本編輯事件、畫布事件、圖元屬性改變事件、分組操作事件、鎖定操作事件、層次操作事件等。
事件處理器不會對UI直接產生作用,它們會調用各種管理器去完成任務。每種元素都會有一個管理器,包括畫布、光標、歷史記錄、整圖、選擇、狀態、選框、文字、變換等。
管理器中會使用到一些比較復雜的操作或計算,這些功能由工具類提供。例如幾何計算、思維導圖位置計算、path數據生成等。
管理器最后會對UI管理器進行操作,達到改變UI的最終效果。
以下,我們以鼠標點擊一個矩形為例,說明框架的運行細節。
HTML中定義了一個選擇框,包括一個虛線矩形和八個實線小矩形。
上述圖元注冊到UI管理器中。
當鼠標點擊矩形時,觸發了MouseDown事件,這個事件會調用鼠標事件處理器。
鼠標事件處理器利用目標工具類,判斷當前鼠標點擊了什么目標。
鼠標事件處理器利用狀態管理器獲取當前狀態,繼而改變當前狀態。
鼠標事件處理器利用光標管理器修改當前光標。
鼠標事件處理器利用選框管理器,顯示選擇框。
選框管理器調用UI管理器中注冊的元素,把選擇框顯示出來。
2.2 圖元數據定義
圖元數據描述是本項目開發的一個難點。圖元數據的特點包括:
(1)圖元種類眾多,每種圖元有自己獨特的屬性。
(2)某些種類的圖元存在公共的屬性。例如矩形、圖片的位置都是通過一個矩形來描述的。
(3)管理器對圖元有共同的操作。例如把圖片數據轉化為可視HTML,鼠標拖動時的平移動作等。這些操作在圖元內部的實現并不一致。
(4)圖元存在一些即使而不需要保存的屬性,例如是否選中,這是一個不需要保存,每次打開時都有默認值的屬性。
在本項目中,圖元具有以下的數據定義:
class Component {public id: string = "";public type: ComponentType = "rect";public props: { [key: string]: string } = {};public connectAnchors: Array<AnchorDirPos> = []; }這就是圖元的基類??梢钥吹?#xff0c;屬性是相當少的。在這里,需要說明幾種跟TypeScript語法相關的設計特性:
(1)圖元基類只有屬性,沒有方法。這是因為圖元需要進行復制操作,復制時方法是無法實現的。
(2)很多特有的屬性被設計到props屬性中,這是一個Object。實際上Map也能實現相同的功能,但因為Map同樣不能被復制,所以使用Object。props中只存放字符串類型的屬性,其他屬性(例如位置)如果使用字符串,還需要轉換,浪費資源。
對于存在公共屬性的圖元,定義了繼承Component的新的基類。例如,用矩形去描述位置的圖元,都繼承于以下的基類:
class RectPositionComponent extends Component {public position: Rect = new Rect(0, 0, 0, 0);public originPosition: Rect = new Rect(0, 0, 0, 0); ?public mindChildren1: Array<MindElement> = [];public mindChildren2: Array<MindElement> = []; ?public constructor() {super(); ?this.props[PreDefine.MindType] = PreDefine.DefaultMindType;} }在這里,我們可以看到,這個類有一個position屬性,它是一個矩形。還有一個originPosition屬性,是用在變換圖元時標記原始位置的屬性。
這個基類還描述了思維導圖的數據結構。本項目的思維導圖,只能在矩形類型的圖元下創建。思維導圖存在樹在兩側的展示方式,所以有兩棵樹(mindChildren1和mindChildren2)去描述思維導圖的結點。
以下是矩形類的定義:
class RectComponent extends RectPositionComponent {public constructor() {super(); ?this.type = "rect";this.id = CommonFunc.GetId(); ?this.props[PreDefine.Fill] = PreDefine.DefaultFill;this.props[PreDefine.Stroke] = PreDefine.DefaultStroke;this.props[PreDefine.StrokeWidth] = PreDefine.DefaultStrokeWidth;this.props[PreDefine.StrokeDash] = PreDefine.DefaultStrokeDash;this.props[PreDefine.CornerRadius] = PreDefine.DefaultCornerRadius; ?this.props[PreDefine.TextHorAlign] = PreDefine.DefaultTextHorAlign;this.props[PreDefine.TextVerAlign] = PreDefine.DefaultTextVerAlign;this.props[PreDefine.FontFamily] = PreDefine.DefaultFontFamily;this.props[PreDefine.FontSize] = PreDefine.DefaultFontSize;this.props[PreDefine.FontColor] = PreDefine.DefaultFontColor;this.props[PreDefine.FontBold] = PreDefine.DefaultFontBold;this.props[PreDefine.FontUnderline] = PreDefine.DefaultFontUnderline;} ?public textPos: AnchorPos = new AnchorPos();public textExist: boolean = false;public textHtml: string = ""; }矩形的樣式,需要通過填充顏色、描邊寬度等屬性去定義。另外,矩形中還有文字,文字又有字體大小、字體顏色等屬性。這些屬性都在圖元初始化時添加到props屬性中。而文字的位置等屬性,由于其存在一定的數據結構,如果定義在props中,還需要轉化,浪費資源,所以獨立定義。事實上,這種區分定義還有一個重要原因,就是在菜單中能修改的屬性都在props中。
對圖元的操作由一個接口去定義。以下是該接口的部分方法:
interface IComponentOperate {ChangeId(): void;Create(obj: unknown): void; ?Html(): string;UpdateHtml(): void;DeleteHtml(): void; ?Move(diagramX: number, diagramY: number): void;MoveOffset(offsetX: number, offsetY: number): void;UpdateInnerPosition(): void;MarkOrigin(): void;ChangeCoordinate(origin: Rect, target: Rect): void; }Html()方法是返回圖元的HTML內容。例如矩形使用一個<rect>標簽定義,形狀一般使用<path>標簽定義,而圖片使用<image>標簽定義。又如,UpdateInnerPosition()方法一般是用在組類型的圖元上的。一個組進行變換之后,其內部的所有圖元都要跟隨變換。但矩形這種圖形,內容并沒有子,也就不需要執行額外的操作。
操作方法跟圖元,通過圖元的ID和類型進行綁定。圖元的類型確定了接口實例化的方法類,而方法類要處理哪個圖元,又通過ID去確定。
總結
以上是生活随笔為你收集整理的在线流程图和思维导图开发技术详解(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html鼠标悬浮缩放,CSS3鼠标悬浮过
- 下一篇: 签到活动随笔