Node

14 min read

Node 是所有节点的基类,继承自 Cell,并定义了节点的通用属性和方法。

constructor

constructor(metadata?: Node.Metadata)

其中 Node.Metadata 是创建节点的选项,除了从 Cell 继承markupattrszIndex 等选项外,还支持以下选项。

选项类型默认值必选描述
size{ width: number; height: number }{ width: 1, height: 1 }节点大小。
position{ x: number; y: number }-节点位置。
anglenumber-节点的旋转角度。
portsobject-链接桩。
portMarkupMarkupobject链接桩的 DOM 结构。
portLabelMarkupMarkupobject链接桩标签的 DOM 结构。

size

节点大小,是一个包含 widthheight 属性的对象,可以通过 size(...) 方法来获取和设置节点大小。

position

节点位置,是一个包含 xy 属性的对象,可以通过 position(...) 方法来获取和设置节点位置。

angle

节点的旋转角度,旋转中心为节点的中心,可以通过 rotate(...) 方法来获取和设置节点的旋转角度。

ports

链接桩是节点上的固定连接点,很多图应用都有链接桩,并且有些应用还将链接桩分为输入链接桩和输出连接桩。

链接桩选项 ports 是一个复杂对象,可以像下面这样使用。

const node = new Node({
  ports: {
    group: { ... }, // 链接桩组定义
    items: [ ... ], // 链接桩
  }
})

或者

const node = new Node({
  ports: [ ... ], // 链接桩
})

通常我们将具有相同行为和外观的链接桩归为同一组,并通过 group 选项来设置分组,该选项是一个对象 { [groupName: string]: PortGroupMetadata },组名为键,值为每组链接桩的默认选项,支持的选项如下:

interface PortGroupMetadata {
  /**
   * 链接桩 DOM 结构定义。
   */
  markup?: Markup
  
  /**
   * 属性和样式。
   */
  attrs?: Attr.CellAttrs
  
  /**
   * 链接桩的 DOM 层级,值越大层级越高。
   */
  zIndex?: number | 'auto'

  /**
   * 群组中链接桩的布局。
   */
  position?: 
    | [number, number] // 绝对定位
    | string           // 链接桩布局方法的名称
    | {                // 链接桩布局方法的名称和参数
        name: string
        args?: object
      }
  
  /**
   * 链接桩标签。
   */
  label?: {
    markup?: Markup
    position?: {    // 链接桩标签布局
      name: string  // 布局名称
      args?: object // 布局参数
    }
  }
}

例如:

const node = new Node({
  ports: {
    group: {
      group1: { 
        markup: { tagName: 'circle' },
        attrs: { },
        zIndex: 1,
        position: {
          name: 'top',
          args: {},
        },
      },
      group2: { ... },
      group3: { ... },
    },
    items: [ ... ],
  }
})

另一个选项 items 是一个数组 PortMetadata[],数组的每一项表示一个链接桩,链接桩支持的选项如下:

interface PortMetadata {
  /**
   *  链接桩唯一 ID,默认自动生成。
   */ 
  id?: string   
  
  /**
   * 分组名称,指定分组后将继承分组中的链接桩选项。
   */
  group?: string
  
  /**
   * 为群组中指定的链接桩布局算法提供参数。
   * 我们不能为单个链接桩指定布局算法,但可以为群组中指定的布局算法提供不同的参数。
   */
  args?: object

  /**
   * 链接桩的 DOM 元素和结构定义。指定该选项后将覆盖 `group` 指代的群组提供的默认选项。
   */
  markup?: Markup
  
  /**
   * 元素的属性样式。指定该选项后将覆盖 `group` 指代的群组提供的默认选项。
   */
  attrs?: Attr.CellAttrs
  
  /**
   * 链接桩的 DOM 层级,值越大层级越高。指定该选项后将覆盖 `group` 指代的群组提供的默认选项。
   */
  zIndex?: number | 'auto'

  /**
   * 链接桩标签。指定该选项后将覆盖 `group` 指代的群组提供的默认选项。
   */
  label?: {
    markup?: Markup
    position?: {    // 链接桩标签布局
      name: string  // 布局名称
      args?: object // 布局参数
    }
  }
}

例如:

const node = new Node({
  ports: {
    group: { ... },
    items: [
      { id: 'port1', group: 'group1', ... },
      { id: 'port2', group: 'group1', ... },
      { id: 'port3', group: 'group2', ... },
    ],
  }
})

更多详情请参考配置链接桩文档。

portMarkup

链接桩的 DOM 结构。当 ports.groupsports.items 都没有为对应的链接桩指定 markup 时,则使用这个默认选项来渲染链接桩,其默认值为:

{
  tagName: 'circle',
  selector: 'circle',
  attrs: {
    r: 10,
    fill: '#fff',
    stroke: '#000',
  },
}

表示用半径为 10px 的圆来渲染链接桩。

portLabelMarkup

链接桩标签的 DOM 结构。当 ports.groupsports.items 都没有为对应的链接桩标签指定 markup 时,则使用这个默认选项来渲染链接桩标签,其默认值为:

{
  tagName: 'text',
  selector: 'text',
  attrs: {
    fill: '#000000',
  },
}

prototype

通用

isNode()

isNode(): true

判断是不是节点,该方法始终返回 true

getBBox(...)

getBBox(options: { deep?: boolean }): Rectangle

获取节点的包围盒。

需要注意的是,该方法通过节点的大小和位置计算包围盒,并不是渲染到画布后的包围盒,涉及的计算只是一些算数运算。

参数

名称类型必选默认值描述
options.deepbooleanfalsetrue 时表示包含所有子节点和边的包围盒,默认为 false

用法

const rect1 = node.getBBox()
const rect2 = node.getBBox({ deep: true })

大小 Size

size(...)

/**
 * 获取节点大小。
 */
size(): Size

/**
 * 设置节点大小。
 */
size(size: Size, options?: Node.ResizeOptions): this

/**
 * 设置节点大小。
 */
size(width: number, height: number, options?: Node.ResizeOptions): this

用法

获取节点大小。

const size = node.size()
console.log(size.width, size.height)

设置节点大小的参数和使用方法与 resize 方法一样。

resize(...)

改变节点大小。

根据旋转角度和 options.direction 的不同,节点位置和大小都可能发生改变。

参数

名称类型必选默认值描述
widthnumber节点宽度。
heightnumber节点高度。
options.directionDirection'bottom-right'向哪个方向改变大小,默认左上角固定,往右下角改变节点大小。
options.silentbooleanfalsetrue 时不触不触发 'change:size''change:position' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

支持向 8 个方向改变节点大小,默认为 'bottom-right' 表示左上角固定,往右下角改变节点大小。

  • top
  • right
  • bottom
  • left
  • top-left
  • top-right
  • bottom-left
  • bottom-right

用法

node.resize(100, 40)

// 右下角固定,向左上角改变大小
node.resize(100, 40, { direction: 'top-left' })

// 不触发事件和重绘
node.resize(100, 40, { silent: true })

scale(...)

scale(
  sx: number,
  sy: number,
  origin?: Point.PointLike,
  options?: Node.SetOptions,
): this

缩放节点。

根据缩放中心和缩放比例不同,节点的大小和位置都可能发生改变。

参数

名称类型必选默认值描述
sxnumberX 轴方向的缩放比例。
synumberY 轴方向的缩放比例。
originPoint.PointLike | null-缩放中心,默认为节点中心。
options.silentbooleanfalsetrue 时不触不触发 'change:size''change:position' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

node.scale(1.5, 1.5)

// 自定义缩放中心
node.scale(1.5, 1.5, { x: 100, y: 30 })

// 不触发事件和重绘
node.scale(1.5, 1.5, null, { silent: true })

fit(...)

fit(options?: Node.FitEmbedsOptions): this

根据子节点和边的大小位置,自动调整节点的大小和位置,使所有子节点和边都位于节点的包围盒内。

参数

名称类型必选默认值描述
options.paddingnumber | { top: number; right: number; bottom: number; left: number }0边距。
options.deepbooleanfalse是否包含所有后代节点和边,默认只包含直接子节点和边。
options.silentbooleanfalsetrue 时不触不触发事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

node.fit()
node.fit({ padding: 10 })
node.fit({ padding: { top: 20, bottom: 20, left: 10, right: 10 }})

位置 Position

position(...)

/**
 * 获取节点位置。
 */
position(options?: Node.GetPositionOptions): Point.PointLike

/**
 * 设置节点位置。
 */
position(x: number, y: number, options?: Node.SetPositionOptions): this

获取节点位置。

参数

名称类型必选默认值描述
options.relativebooleanfalse是否返回相对于父节点的相对位置,默认为 false 表示返回节点相对于画布的绝对位置。

用法

const pos = rect.position()
console.log(pos.x, pos.y)

const relativePos = child.position({ relative: true })
console.log(relativePos.x, relativePos.y)

设置节点位置。

参数

名称类型必选默认值描述
xnumber节点绝对或相对 X 轴坐标。
ynumber节点绝对或相对 Y 轴坐标。
options.relativebooleanfalse提供的坐标是否为相对坐标。为 true 时表示提供的坐标为相对于父节点位置的坐标,默认为 false 表示提供的坐标为相对于画布的绝对坐标。
options.deepbooleanfalse是否同时改变子节点/边的位置。
options.silentbooleanfalsetrue 时不触不触发 'change:position' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

默认使用绝对坐标,当 options.relativetrue 时表示使用相对坐标。

// 将节点移动到画布 [30, 30] 位置处
node.position(30, 30)

// 将子节点移动到相对父节点左上角 [30, 30] 位置处
child.position(30, 30, { relative: true })

options.deeptrue 时将同时移动子节点和边。

parent.position(30, 30)

options.silenttrue 时不触不触发 'change:position' 事件和画布重绘。

node.position(30, 30, { silent: true })

在选项中支持其他自定义键值对,可以在事件回调用使用。

node.position(30, 30, { otherKey: 'otherValue', ... })

translate(...)

translate(tx?: number, ty?: number, options?: Node.TranslateOptions): this

平移节点以及节点包含的子节点和边。

参数

名称类型必选默认值描述
txnumber0节点在 X 轴的偏移量。
tynumber0节点在 Y 轴的偏移量。
options.restrictRectangle.RectangleLikeundefined将节点的可移动范围限制在指定的矩形区域内。
options.transitionboolean | Animation.Optionsfalse是否使用动画或指定一个动画选项
options.silentbooleanfalsetrue 时不触不触发 'change:position' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

当指定的 txtyundefined 时,表示对应方向的平移量为 0

node.translate(30, 30)
node.translate(30)            // 只在 X 轴移动
node.translate(undefined, 30) // 只在 Y 轴移动

我们可以通过 options.restrict 选项来将节点的移动限制在指定的矩形 {x: number; y: number; width: number; height: number} 范围内。

例如,我们可以将子节点的移动限制在父节点的包围盒中:

child.translate(30, 30, { 
  restrict: child.getParent().getBBox(), 
})

options.transitiontrue 或指定了一个动画选项时,表示使用动画来平移节点,详情请参考使用动画文档

 // 使用默认动画在平移节点
node.translate(30, 30, {
  transition: true,
})

// 自定动画选项
node.translate(30, 30, {
  transition: {
    duration: 2000,
  },
})

options.silent 为 true 时不触不触发 'change:position' 事件和画布重绘。

node.translate(30, 30, { silent: true })

在选项中支持其他自定义键值对,可以在事件回调用使用。

node.translate(30, 30, { otherKey: 'otherValue', ... })

旋转角度 Angle

getAngle()

getAngle(): number

获取节点的旋转角度。

用法

if (node.getAngle() !== 0){
  // do something
}

rotate(...)

rotate(
  deg: number, 
  absolute?: boolean, 
  origin?: Point.PointLike, 
  options?: Node.RotateOptions,
): this

旋转节点。

参数

名称类型必选默认值描述
degnumber旋转度数。
options.absolutebooleanfalsetrue 时表示给定的度数为节点旋转后的绝对度数,默认为 false,表示节点在当前旋转角度的基础上再旋转给定的度数。
options.centerPoint.PointLikeundefined默认沿节点中心旋转,当给定 options.center 后表示沿给定的中心旋转。
options.silentbooleanfalsetrue 时不触不触发 'change:angle' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

默认为相对旋转,即在当前旋转角度的基础上在旋转给定的度数,当 options.absolutetrue 时表示绝对旋转,即给定的度数为节点旋转后的度数。

// 相对旋转,节点在当前旋转角度的基础上在旋转 30 度
node.rotate(30)

// 绝对旋转,节点旋转后的角度为 30 度
node.rotate(30, { absolute: true }) 

默认沿节点中心旋转,可以通过 options.center 选项来指定一个旋转中心。

node.rotate(30, { center: { x: 10, y: 10 }})

默认触发 'change:angle' 事件和画布重绘,当 options.silenttrue 时,不触发 'change:angle' 事件和画布重绘。

node.rotate(30, { silent: true })

在选项中支持其他自定义键值对,可以在事件回调用使用。

node.rotate(30, { otherKey: 'otherValue', ... })

链接桩 Ports

链接桩是节点上的固定连接点,很多图应用都有链接桩,并且有些应用还将链接桩分为输入链接桩和输出连接桩。

在上面我们介绍了链接桩的数据结构,这里我们将继续介绍节点上操作链接桩的一些方法。

addPort(...)

addPort(port: PortMetadata, options?: Node.SetOptions): this

添加单个链接桩。链接桩被添加到链接桩数组末尾。

参数

名称类型必选默认值描述
portPortMetadata链接桩。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

addPorts(...)

addPorts(ports: PortMetadata[], options?: Node.SetOptions)

添加多个链接桩。链接桩被添加到链接桩数组末尾。

参数

名称类型必选默认值描述
portPortMetadata[]链接桩数组。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

insertPort(...)

insertPort(index: number, port: PortMetadata, options?: Node.SetOptions): this

在指定位置添加连接桩。

参数

名称类型必选默认值描述
indexnumber连接桩位置。
portPortMetadata链接桩。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

hasPort(...)

hasPort(portId: string): boolean

检查指定的链接桩是否存在。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。

用法

if (node.hasPort('port1')) {
  // do something
}

hasPorts()

hasPorts(): boolean

检查节点是否包含链接桩。

用法

if (node.hasPorts()) {
  // do something
}

getPort(...)

getPort(portId: string): PortMetadata

根据链接桩 ID 获取链接桩。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。

getPortAt(...)

getPortAt(index: number): PortMetadata | null

获取指定索引位置的链接桩。

参数

名称类型必选默认值描述
indexnumber链接桩索引。

getPorts()

getPorts(): PortMetadata[]

获取所有链接桩。

getPortsByGroup(...)

getPortsByGroup(groupName: string): PortMetadata[]

获取群组下的所有链接桩。

参数

名称类型必选默认值描述
groupNamestring群组名称。

removePort(...)

/**
 * 删除指定的链接桩。 
 */
removePort(port: PortMetadata, options?: Node.SetOptions): this

/**
 * 删除指定 ID 的链接桩。 
 */
removePort(portId: string, options?: Node.SetOptions): this

删除指定的链接桩。

参数

名称类型必选默认值描述
portPortMetadata链接桩。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

删除指定 ID 的链接桩。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

removePortAt(...)

removePortAt(index: number, options?: Node.SetOptions): this

删除指定索引位置的链接桩。

参数

名称类型必选默认值描述
indexnumber链接桩索引。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

removePorts(...)

/**
 * 删除所有链接桩。 
 */
removePorts(options?: Node.SetOptions): this

/**
 * 删除指定的链接桩。
 */
removePorts(ports: (PortMetadata | string)[], options?: Node.SetOptions): this

删除指定的多个链接桩,当指定的链接桩为 null 时,删除所有链接桩。

参数

名称类型必选默认值描述
ports(PortMetadata | string)[]要删除的链接桩数组。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

getPortIndex(...)

getPortIndex(port: PortMetadata | string): number

获取链接桩的索引位置。

getPortProp(...)

getPortProp<T>(portId: string, path?: string | string[]): any

获取链接桩指定路径上的属性值。当路径为空时,返回完整的链接桩。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
pathstring | string[]属性路径。
pathstring 类型时,路径是以 '\' 分割的字符串。
pathstring[] 类型时,路径是链接桩对象路径上的 Key 构成的数组。

用法

node.getPortProp('port1')
node.getPortProp('port1', 'attrs/circle')
node.getPortProp('port1', ['attrs', 'circle'])

setPortProp(...)

setPortProp(
  portId: string,
  path: string | string[],
  value: any,
  options?: Node.SetOptions,
): this

根据路径设置链接桩的属性。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
pathstring | string[]属性路径。
pathstring 类型时,路径是以 '\' 分割的字符串。
pathstring[] 类型时,路径是链接桩对象路径上的 Key 构成的数组。
valueany属性值。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

node.setPortProp('port1', 'attrs/circle', { fill: '#ffffff', stroke: '#000000' })
node.setPortProp('port1', ['attrs', 'circle'],  { fill: '#ffffff', stroke: '#000000' })
setPortProp(
  portId: string,
  value: DeepPartial<PortMetadata>,
  options?: Node.SetOptions,
): this

设置链接桩的属性,提供的属性选项与当前值进行深度 merge

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
valueDeepPartial<PortMetadata>链接桩选项。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

用法

node.getPortProp('port1', {
  attrs: {
    circle:{ 
      fill: '#ffffff', 
      stroke: '#000000',
    },
  },
})

removePortProp(...)

removePortProp(portId: string, options?: Node.SetOptions): this

删除指定链接桩的选项。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。
removePortProp(portId: string, path: string | string[], options?: Node.SetOptions): this

删除指定链接桩和指定路径的选项。

参数

名称类型必选默认值描述
portIdstring链接桩 ID。
pathstring | string[]属性路径。
pathstring 类型时,路径是以 '\' 分割的字符串。
pathstring[] 类型时,路径是链接桩对象路径上的 Key 构成的数组。
options.silentbooleanfalsetrue 时不触发 'change:ports' 事件和画布重绘。
options...othersobject其他自定义键值对,可以在事件回调中使用。

portProp(...)

portProp(portId: string): PortMetadata
portProp<T>(portId: string, path: string | string[]): T
portProp(
  portId: string,
  path: string | string[],
  value: any,
  options?: Node.SetOptions,
): this
portProp(
  portId: string,
  value: DeepPartial<PortMetadata>,
  options?: Node.SetOptions,
): this

该方法是 getPortPropsetPortProp 两个方法的集合,参数选项和使用方法与这两个方法一致。