antv-x6 源码解析(2) - markup

上一章:antv-x6 源码解析(1) - x6 核心包解析

markup

markup 是视图的一部分,指定了渲染时使用的 SVG/HTML 片段。

定义

Markup 的定义如下:

ts 复制代码
export type Markup = string | Markup.JSONMarkup | Markup.JSONMarkup[]

export namespace Markup {
  export interface JSONMarkup {
    /**
     * The namespace URI of the element. It defaults to SVG namespace
     * `"http://www.w3.org/2000/svg"`.
     */
    ns?: string | null
    /**
     * The type of element to be created.
     */
    tagName: string
    /**
     * A unique selector for targeting the element within the `attr`
     * cell attribute.
     */
    selector?: string | null
    /**
     * A selector for targeting multiple elements within the `attr`
     * cell attribute. The group selector name must not be the same
     * as an existing selector name.
     */
    groupSelector?: string | string[] | null
    attrs?: Attr.SimpleAttrs
    style?: Record<string, string | number>
    className?: string | string[]
    children?: JSONMarkup[]
    textContent?: string
  }
}

markup 在渲染前会被 parseJSONMarkup 解析为 ParseResult, 它的定义如下:

ts 复制代码
export interface ParseResult {
    fragment: DocumentFragment
    selectors: Selectors
    groups: KeyValue<Element[]>
}

GraphView

我们从 GraphViewmarkup 入手来理解这两者间的关系,我们首先看它的定义:

ts 复制代码
  const prefixCls = `${Config.prefixCls}-graph` // `Config.prefixCls` 默认值是 `x6`,即它的值是 `x6-graph`

  export const markup: Markup.JSONMarkup[] = [
    {
      ns: Dom.ns.xhtml, // `http://www.w3.org/1999/xhtml`
      tagName: 'div',
      selector: 'background',
      className: `${prefixCls}-background`,
    },
    {
      ns: Dom.ns.xhtml,
      tagName: 'div',
      selector: 'grid',
      className: `${prefixCls}-grid`,
    },
    {
      ns: Dom.ns.svg, // http://www.w3.org/2000/svg
      tagName: 'svg',
      selector: 'svg',
      className: `${prefixCls}-svg`,
      attrs: {
        width: '100%',
        height: '100%',
        'xmlns:xlink': Dom.ns.xlink, // http://www.w3.org/1999/xlink
      },
      children: [
        {
          tagName: 'defs',
          selector: 'defs',
        },
        {
          tagName: 'g',
          selector: 'viewport',
          className: `${prefixCls}-svg-viewport`,
          children: [
            {
              tagName: 'g',
              selector: 'primer',
              className: `${prefixCls}-svg-primer`,
            },
            {
              tagName: 'g',
              selector: 'stage',
              className: `${prefixCls}-svg-stage`,
            },
            {
              tagName: 'g',
              selector: 'decorator',
              className: `${prefixCls}-svg-decorator`,
            },
            {
              tagName: 'g',
              selector: 'overlay',
              className: `${prefixCls}-svg-overlay`,
            },
          ],
        },
      ],
    },
  ]

它的渲染结果如下:

对于其中的 selector 字段,它所在的元素会被加入映射表 selectors 中,其 key 就是 selector 的值。

我们现在重新看 GraphView 的实现,如下:

ts 复制代码
export class GraphView extends View {
  public readonly container: HTMLElement
  public readonly background: HTMLDivElement
  public readonly grid: HTMLDivElement
  public readonly svg: SVGSVGElement
  public readonly defs: SVGDefsElement
  public readonly viewport: SVGGElement
  public readonly primer: SVGGElement
  public readonly stage: SVGGElement
  public readonly decorator: SVGGElement
  public readonly overlay: SVGGElement

  constructor(protected readonly graph: Graph) {
    super()

    const { selectors, fragment } = Markup.parseJSONMarkup(GraphView.markup)
    this.background = selectors.background as HTMLDivElement
    this.grid = selectors.grid as HTMLDivElement
    this.svg = selectors.svg as SVGSVGElement
    this.defs = selectors.defs as SVGDefsElement
    this.viewport = selectors.viewport as SVGGElement
    this.primer = selectors.primer as SVGGElement
    this.stage = selectors.stage as SVGGElement
    this.decorator = selectors.decorator as SVGGElement
    this.overlay = selectors.overlay as SVGGElement
    this.container = this.options.container
    this.restore = GraphView.snapshoot(this.container)

    Dom.addClass(this.container, this.prefixClassName('graph'))
    Dom.append(this.container, fragment)

    this.delegateEvents()
  }
}

GraphView.markup 被解析后生成了 selectors,它存储的是元素的映射表,GraphView 将其一一取出,作为自身的属性,这使得我们可以通过属性来直接访问它的 Dom 元素。

NodeView

我们再来看 NodeViewmarkup 是如何渲染的,NodeViewmarkuprenderMarkup 中渲染,如下:

ts 复制代码
  protected renderMarkup() {
    const markup = this.cell.markup
    if (markup) {
      if (typeof markup === 'string') {
        throw new TypeError('Not support string markup.')
      }

      return this.renderJSONMarkup(markup)
    }

    throw new TypeError('Invalid node markup.')
  }

  protected renderJSONMarkup(markup: Markup.JSONMarkup | Markup.JSONMarkup[]) {
    const ret = this.parseJSONMarkup(markup, this.container)
    this.selectors = ret.selectors
    this.container.appendChild(ret.fragment)
  }

它从 cell 中获取 markup, 然后将其解析为 selectorsfragment,最后 fragment 被添加到 container

EdgeViewmarkup 的渲染与 NodeView 一致,此处不再赘述。

相关推荐
南方kenny3 分钟前
React 魔法揭秘:useRef 与 forwardRef 的奇妙之旅
前端·javascript·react.js
海上生明月丿11 分钟前
Vue 工程化
前端·javascript·vue.js
香蕉可乐荷包蛋2 小时前
排序算法 (Sorting Algorithms)-JS示例
javascript·算法·排序算法
格调UI成品2 小时前
元宇宙工厂前端新形态:Three.js与WebGL实现3D产线交互的轻量化之路
前端·javascript·webgl
gnip2 小时前
微前端框架选型
前端·javascript
一只小风华~3 小时前
JavaScript:数组常用操作方法的总结表格
前端·javascript·数据结构·vue.js·算法
前端老鹰3 小时前
JavaScript Array.prototype.some ():数组判断的 “快捷侦探”
前端·javascript
程序媛李李李李李蕾3 小时前
你不能直接用现成的吗?整个前端做笔记管理工具真是折腾人
javascript·vue.js·后端
整点可乐3 小时前
cesium实现鹰眼图
javascript·cesium
艾小码3 小时前
Web存储指南:彻底掌握localStorage与sessionStorage
javascript