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 一致,此处不再赘述。

相关推荐
To_OC1 天前
LC 35 搜索插入位置:二分查找谁都会,边界条件谁写谁懵
javascript·算法·leetcode
码哥DFS1 天前
二叉树的直径
开发语言·javascript·算法
光影少年1 天前
RN的Fabric 渲染流程
运维·前端·javascript·react native·react.js·fabric
用户938515635071 天前
React Router 进阶:路由守卫、登录鉴权与状态传递
前端·javascript·全栈
kyriewen1 天前
我重写了自己用了两年的防抖节流Hook——发现里面藏着3个隐藏bug
前端·javascript·面试
漂流瓶jz1 天前
Webpack开发环境:观察模式/webpack-dev-server/HMR热更新
前端·javascript·webpack
小爬的老粉丝1 天前
Vue 3 文件预览生产排障:Worker/WASM 404、鉴权 Blob 与子路径
javascript·vue.js·wasm
亲亲小宝宝鸭1 天前
离谱架构系列——我在祖传代码里发现了【智子】
javascript
触底反弹1 天前
🚀 20 行代码手写前端路由 + React Router 核心 API 一篇搞定!
前端·javascript·react.js
布兰妮甜1 天前
Vue 状态管理选型:Pinia 完整实战,对比 Vuex,模块化持久化
前端·javascript·vue.js·pinia·vuex