AntV|AntV X6源码简析

前言 AntV是蚂蚁金服全新一代数据可视化解决方案,其中X6主要用于解决图编辑领域相关的解决方案,其是一款图编辑引擎,内置了一下编辑器所需的功能及组件等,本文旨在通过简要分析x6源码来对图编辑领域的一些底层引擎进行一个大致了解,同时也为团队中需要进行基于X6编辑引擎进行构建的图编辑器提供一些侧面了解,在碰到问题时可以较快的找到问题点。
架构 AntV|AntV X6源码简析
文章图片

AntV|AntV X6源码简析
文章图片

X6整体是基于MVVM的架构进行设计的,对外整体暴露Graph的类,其中的Node、Edge、Port等都有对外暴露的方法,可以单独使用,其中提供了类Jquery的一些dom操作方法,整体的Graph基于了一个事件基类,对事件进行的整体的处理,其中使用了dispose来对实例进行显示判定。
AntV|AntV X6源码简析
文章图片

AntV|AntV X6源码简析
文章图片

整体设计符合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 { 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) { 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 { get label() { return this.getLabel() }set label(val: string | undefined | null) { this.setLabel(val) }getLabel() { return this.getAttrByPath('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 { public readonly collection: Collection protected readonly batches: KeyValue = {} protected readonly addings: WeakMap = new WeakMap() public graph: Graph | null protected nodes: KeyValue = {} protected edges: KeyValue = {} protected outgoings: KeyValue = https://www.it610.com/article/{} protected incomings: KeyValue = {}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 protected zPivots: KeyValue 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 extends Basecoat>{ protected data: D protected previous: D protected changed: Partial protected pending = false protected changing = false protected pendingOptions: Store.MutateOptions | null protected mutate() {} constructor(data: Partial = {}) { super() this.data = https://www.it610.com/article/{} as D this.mutate(ObjectExt.cloneDeep(data)) this.changed = {} } get() {} set() {} remove() {} clone() {} }

View
聚合EdgeView、CellView等,使用了jQuery的相关DOM操作
abstract class View extends Basecoat { public readonly cid: string public container: Element protected selectors: Markup.Selectorspublic get priority() { return 2 }constructor() { super() this.cid = Private.uniqueId() View.views[this.cid] = this } }

Geometry
【AntV|AntV X6源码简析】提供几何图形的操作处理,包括Curve、Ellipse、Line、Point、PolyLine、Rectangle、Angle等
abstract class Geometry { abstract scale( sx: number, sy: number, origin?: Point.PointLike | Point.PointData, ): thisabstract rotate( angle: number, origin?: Point.PointLike | Point.PointData, ): thisabstract translate(tx: number, ty: number): thisabstract translate(p: Point.PointLike | Point.PointData): thisabstract equals(g: any): booleanabstract clone(): Geometryabstract toJSON(): JSONObject | JSONArrayabstract serialize(): stringvalueOf() { return this.toJSON() }toString() { return JSON.stringify(this.toJSON()) } }

Registry
提供注册中心的机制,
class Registry< Entity, Presets = KeyValue, OptionalType = never, > { public readonly data: KeyValue public readonly options: Registry.Optionsconstructor(options: Registry.Options) { this.options = { ...options } this.data = https://www.it610.com/article/(this.options.data as KeyValue) || {} 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 { 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

    推荐阅读