前言
AntV是蚂蚁金服全新一代数据可视化解决方案,其中X6次要用于解决图编辑畛域相干的解决方案,其是一款图编辑引擎,内置了一下编辑器所需的性能及组件等,本文旨在通过简要剖析x6源码来对图编辑畛域的一些底层引擎进行一个大抵理解,同时也为团队中须要进行基于X6编辑引擎进行构建的图编辑器提供一些侧面理解,在碰到问题时能够较快的找到问题点。
架构
X6整体是基于MVVM的架构进行设计的,对外整体裸露Graph的类,其中的Node、Edge、Port等都有对外裸露的办法,能够独自应用,其中提供了类Jquery的一些dom操作方法,整体的Graph基于了一个事件基类,对事件进行的整体的解决,其中应用了dispose来对实例进行显示断定。
整体设计合乎SOLID准则,提供事件机制进行公布订阅解耦,对于扩展性构造则提供注册机制,进行扩展性插件组织
目录
整体采纳monorepo进行源码的仓库治理
packages
x6
- addon
- common
- geometry
- global
- graph
- layout
- model
- registry
- shape
- style
- types
- util
- view
- x6-angular-shape
x6-geometry
- angle
- curve
- ellipse
- line
- point
- polyline
- rectangle
- x6-react
- x6-react-components
- x6-react-shape
- x6-vector
- x6-vue-shape
源码
从架构档次能够看出,整体对外裸露的就是Graph这么一个大类,因此在剖析源码调用过程中,咱们抓住Graph进行逐渐的往外拓展,从而把握整体的一个设计链路,防止陷入部分无奈抽离
Graph
Graph类提供了整体所有构造的汇总,从而裸露给用户
class Graph extends Basecoat<EventArgs> { public readonly options: GraphOptions.Definition public readonly css: CSSManager public readonly model: Model public readonly view: GraphView public readonly hook: HookManager public readonly grid: Grid public readonly defs: Defs public readonly knob: Knob public readonly coord: Coord public readonly renderer: ViewRenderer public readonly snapline: Snapline public readonly highlight: Highlight public readonly transform: Transform public readonly clipboard: Clipboard public readonly selection: Selection public readonly background: Background public readonly history: History public readonly scroller: Scroller public readonly minimap: MiniMap public readonly keyboard: Shortcut public readonly mousewheel: Wheel public readonly panning: Panning public readonly print: Print public readonly format: Format public readonly size: SizeManager // 拿到须要加载的container public get container() { return this.view.container } protected get [Symbol.toStringTag]() { return Graph.toStringTag } constructor(options: Partial<GraphOptions.Manual>) { super() this.options = GraphOptions.get(options) this.css = new CSSManager(this) this.hook = new HookManager(this) this.view = this.hook.createView() this.defs = this.hook.createDefsManager() this.coord = this.hook.createCoordManager() this.transform = this.hook.createTransformManager() this.knob = this.hook.createKnobManager() this.highlight = this.hook.createHighlightManager() this.grid = this.hook.createGridManager() this.background = this.hook.createBackgroundManager() this.model = this.hook.createModel() this.renderer = this.hook.createRenderer() this.clipboard = this.hook.createClipboardManager() this.snapline = this.hook.createSnaplineManager() this.selection = this.hook.createSelectionManager() this.history = this.hook.createHistoryManager() this.scroller = this.hook.createScrollerManager() this.minimap = this.hook.createMiniMapManager() this.keyboard = this.hook.createKeyboard() this.mousewheel = this.hook.createMouseWheel() this.print = this.hook.createPrintManager() this.format = this.hook.createFormatManager() this.panning = this.hook.createPanningManager() this.size = this.hook.createSizeManager() }}
Shape
实现各种类型办法的两头解耦层,用于包裹属性等
// shape的基类,标记shape的各种属性,如标签等class Base< Properties extends Node.Properties = Node.Properties,> extends Node<Properties> { get label() { return this.getLabel() } set label(val: string | undefined | null) { this.setLabel(val) } getLabel() { return this.getAttrByPath<string>('text/text') } setLabel(label?: string | null, options?: Node.SetOptions) { if (label == null) { this.removeLabel() } else { this.setAttrByPath('text/text', label, options) } return this } removeLabel() { this.removeAttrByPath('text/text') return this }}
// 创立shape的办法function createShape( shape: string, config: Node.Config, options: { noText?: boolean ignoreMarkup?: boolean parent?: Node.Definition | typeof Base } = {},) { const name = getName(shape) const defaults: Node.Config = { constructorName: name, attrs: { '.': { fill: '#ffffff', stroke: 'none', }, [shape]: { fill: '#ffffff', stroke: '#000000', }, }, } if (!options.ignoreMarkup) { defaults.markup = getMarkup(shape, options.noText === true) } const base = options.parent || Base return base.define( ObjectExt.merge(defaults, config, { shape: name }), ) as typeof Base}
Model
提供了Node、Cell、Edge、Prot等的解决办法
class Model extends Basecoat<Model.EventArgs> { public readonly collection: Collection protected readonly batches: KeyValue<number> = {} protected readonly addings: WeakMap<Cell, boolean> = new WeakMap() public graph: Graph | null protected nodes: KeyValue<boolean> = {} protected edges: KeyValue<boolean> = {} protected outgoings: KeyValue<string[]> = {} protected incomings: KeyValue<string[]> = {} protected get [Symbol.toStringTag]() { return Model.toStringTag } constructor(cells: Cell[] = []) { super() this.collection = new Collection(cells) this.setup() }}
Renderer
渲染Model相干的数据
class Renderer extends Base { protected views: KeyValue<CellView> protected zPivots: KeyValue<Comment> protected updates: Renderer.Updates protected init() {} protected startListening() {} protected stopListening() {} protected resetUpdates() {} protected onSortModel() {} protected onModelReseted() {} protected onBatchStop() {} protected onCellAdded() {} protected onCellRemove() {} protected onCellZIndexChanged() {} protected onCellVisibleChanged() {} protected processEdgeOnTerminalVisibleChanged() {} protected isEdgeTerminalVisible() {}}
Store
数据的公共存储仓库,与renderer进行交互
class Store<D> extends Basecoat<Store.EventArgs<D>>{ protected data: D protected previous: D protected changed: Partial<D> protected pending = false protected changing = false protected pendingOptions: Store.MutateOptions | null protected mutate<K extends keyof D>() {} constructor(data: Partial<D> = {}) { super() this.data = {} as D this.mutate(ObjectExt.cloneDeep(data)) this.changed = {} } get() {} set() {} remove() {} clone() {}}
View
聚合EdgeView、CellView等,应用了jQuery的相干DOM操作
abstract class View<EventArgs = any> extends Basecoat<EventArgs> { public readonly cid: string public container: Element protected selectors: Markup.Selectors public get priority() { return 2 } constructor() { super() this.cid = Private.uniqueId() View.views[this.cid] = this }}
Geometry
提供几何图形的操作解决,包含Curve、Ellipse、Line、Point、PolyLine、Rectangle、Angle等
abstract class Geometry { abstract scale( sx: number, sy: number, origin?: Point.PointLike | Point.PointData, ): this abstract rotate( angle: number, origin?: Point.PointLike | Point.PointData, ): this abstract translate(tx: number, ty: number): this abstract translate(p: Point.PointLike | Point.PointData): this abstract equals(g: any): boolean abstract clone(): Geometry abstract toJSON(): JSONObject | JSONArray abstract serialize(): string valueOf() { return this.toJSON() } toString() { return JSON.stringify(this.toJSON()) }}
Registry
提供注册核心的机制,
class Registry< Entity, Presets = KeyValue<Entity>, OptionalType = never,> { public readonly data: KeyValue<Entity> public readonly options: Registry.Options<Entity | OptionalType> constructor(options: Registry.Options<Entity | OptionalType>) { this.options = { ...options } this.data = (this.options.data as KeyValue<Entity>) || {} this.register = this.register.bind(this) this.unregister = this.unregister.bind(this) } get names() { return Object.keys(this.data) } register() {} unregister() {} get() {} exist() {}}
Events
提供事件的监听(公布订阅)机制
class Events<EventArgs extends Events.EventArgs = any> { private listeners: { [name: string]: any[] } = {} on() {} once() {} off() {} trigger() {} emit() {}}
总结
整体咱们看到,要想实现一款底层的图编辑引擎,须要做好整体的架构设计及解构,通常不外乎MVC的构造的变种,因此咱们在抉择Model层、View层、Controller层的过程中,能够综合思考软件工程中不同的设计方案来解决,比方对事件零碎的设计、插件机制的设计等等,另外在底层渲染方面,毕竟作为图可视化畛域的前端计划,对SVG、HTML、Canvas等不同计划的抉择也须要针对性思考,以上。可视化畛域深度与广度摸索起来不仅仅局限于前端侧,心愿可能在这方面可能零碎的学习与实际,从而摸索出在前端畛域的一些机会,共勉!!!
参考
- X6官网
- Antv/X6<图编辑引擎>
- x6源码
- antvis G6 vs X6 vs Topology 源码统计分析
- X6 1.0 道歉来晚
- XFlow 1.0:业余的图编辑利用解决方案
- X6:深度打磨,日臻完善
- AntV 旗下图编辑引擎 X6