Connector

阅读时间约 4 分钟

连接器将起点、路由返回的点、终点加工为 <path> 元素的 d 属性,决定了边渲染到画布后的样式。我们在 Registry.Connector.presets 命名空间中提供了以下几种连接器。

连接器说明
normal简单连接器,用直线连接起点、路由点和终点。
smooth平滑连接器,用三次贝塞尔曲线线连接起点、路由点和终点。
rounded圆角连接器,用直线连接起点、路由点和终点,并在线段连接处用圆弧链接(倒圆角)。
jumpover跳线连接器,用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接。

可以为某条边设置路由:

const edge = graph.addEdge({
  source,
  target,
  connector: {
    name: 'rounded',
    args: {
      radius: 20,
    },
  },
})

当没有连接器参数时,可以简化为:

const edge = graph.addEdge({
  source,
  target,
  connector: 'rounded',
})

也可以调用 edge.setConnector 来设置连接器:

edge.setConnector('rounded', { radius: 20 })

在创建画布时,可以通过 connecting 选项来设置全局默认连接器(默认为 'normal'):

new Graph({
  connecting: {
    connector: { 
      name: 'rounded',
      args: {
        radius: 20,
      },
    },
  },
})

当路由没有参数时,也可以简化为:

new Graph({
  connecting: {
    connector: 'rounded',
  },
})

下面我们一起来看看如何使用内置连接器,以及如何自定并注册自定义连接器。

presets

normal

系统的默认连接器,将起点、路由点、终点通过直线按顺序连接。

支持的参数如下表:

参数名参数类型是否必选默认值参数说明
rawbooleanfalse是否返回一个 Path 对象,默认值为 false 返回序列化后的字符串。

smooth

平滑连接器,通过三次贝塞尔链接起点、路由点和终点。

支持的参数如下表:

参数名参数类型是否必选默认值参数说明
rawbooleanfalse是否返回一个 Path 对象,默认值为 false 返回序列化后的字符串。
directionH | V-保持水平连接或者保持垂直连接,不设置会根据起点和终点位置动态计算。

例如:

graph.addEdge({
  source: rect1,
  target: rect2,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
  connector: 'smooth',
})

rounded

圆角连接器,将起点、路由点、终点通过直线按顺序连接,并在线段连接处通过圆弧连接(倒圆角)。

支持的参数如下表:

参数名参数类型是否必选默认值参数说明
radiusnumber10倒角半径。
rawbooleanfalse是否返回一个 Path 对象,默认值为 false 返回序列化后的字符串。

例如:

graph.addEdge({
  source: rect1,
  target: rect2,
  vertices: [
    { x: 100, y: 200 },
    { x: 300, y: 120 },
  ],
  connector: {
    name: 'rounded',
    args: {
      radius: 10,
    },
  },
})

jumpover

跳线连接器,用直线连接起点、路由点和终点,并在边与边的交叉处用跳线符号链接。

支持的参数如下表:

参数名参数类型是否必选默认值参数说明
type'arc' | 'gap' | 'cubic''arc'跳线类型。
sizenumber5跳线大小。
radiusnumber0倒角半径。
rawbooleanfalse是否返回一个 Path 对象,默认值为 false 返回序列化后的字符串。

registry

连接器是一个具有如下签名的函数,返回 Path 对象或序列化后的字符串。

export type Definition<T> = (
  this: EdgeView,                 // 边的视图
  sourcePoint: Point.PointLike,   // 起点
  targetPoint: Point.PointLike,   // 终点
  routePoints: Point.PointLike[], // 路由返回的点
  args: T,                        // 参数
  edgeView: EdgeView,             // 边的视图
) => Path | string
参数名参数类型参数说明
thisEdgeView边的视图。
sourcePointPoint.PointLike起点。
targetPointPoint.PointLike终点。
routePointsPoint.PointLike[]路由返回的点。
argsT连接器参数。
edgeViewEdgeView边的视图。

并在 Registry.Connector.registry 对象上提供了 registerunregister 两个方法来注册和取消注册连接器。

register

register(entities: { [name: string]: Definition }, force?: boolean): void
register(name: string, entity: Definition, force?: boolean): Definition

注册连接器。

unregister

unregister(name: string): Definition | null

取消注册连接器。

自定义连接器

按照上面的规则,我来定义一个 wobble 连接器:

export interface WobbleArgs {
  spread?: number
  raw?: boolean
}

function wobble(
  sourcePoint: Point.PointLike, 
  targetPoint: Point.PointLike, 
  routePoints: Point.PointLike[], 
  args: WobbleArgs,
) {
    const spread = args.spread || 20
    const points = [...vertices, targetPoint].map((p) => Point.create(p))
    let prev = Point.create(sourcePoint)
    const path = new Path(Path.createSegment('M', prev))

    for (let i = 0, n = points.length; i < n; i += 1) {
      const next = points[i]
      const distance = prev.distance(next)
      let d = spread

      while (d < distance) {
        const current = prev.clone().move(next, -d)
        current.translate(
          Math.floor(7 * Math.random()) - 3,
          Math.floor(7 * Math.random()) - 3,
        )
        path.appendSegment(Path.createSegment('L', current))
        d += spread
      }

      path.appendSegment(Path.createSegment('L', next))
      prev = next
    }

    return args.raw ? path : path.serialize()
}

实际上,我们将 Registry.Connector.registry 对象的 registerunregister 方法分别挂载为 Graph 的两个静态方法 Graph.registerConnectorGraph.unregisterConnector,所以我们可以像下面这样来注册连接器:

Graph.registerConnector('wobble', wobble)

注册以后我们就可以通过连接器名称来使用:

edge.setConnector('wobble', { spread: 16 })