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

相关推荐
雪芽蓝域zzs4 小时前
vue解构平铺VS对象包裹
前端·javascript·vue.js
默_笙7 小时前
🚲 从写信到打电话:WebSocket 双工通信与跨域方案的"通信进化史"
前端·javascript
ikoala8 小时前
DeepSeek 官方仓库惊现 DeepSeek Harness 桌面端!
前端·javascript·后端
liangshanbo12158 小时前
React useEffect 与 useLayoutEffect 面试题整理
前端·javascript·react.js
ID34610744209 小时前
【课程设计】基于Spring Boot+Vue的校园共享无人机服务系统设计与实现-计算机毕设 附源码44219
javascript·vue.js·spring boot·python·node.js·php·课程设计
夜不会漫长9 小时前
C++:类和对象(2)
java·javascript·c++
BillKu9 小时前
Vue3 可拖拽分栏布局:两种实现方式详解
javascript·vue.js·ecmascript
雪芽蓝域zzs10 小时前
第三十七节:防抖、节流封装(composables 通用函数,搜索框防重复请求)
前端·javascript·vue.js
十年一梦惊觉醒10 小时前
快手视频发布助手,全自动视频发布带货工具
开发语言·前端·javascript
超人气王10 小时前
细说agent心跳机制--agentLoop循环机制中死循环处理,带你进一步了解agent工作底层原理
javascript·人工智能