前言

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