--- title: 自定义边 order: 2 --- ## 示例 ```js import { Graph as BaseGraph, extend, Extensions } from '@antv/g6'; // 自定义变类型,继承一个已有的边类型或边基类 Extensions.BaseEdge class CustomNode extends Extensions.LineEdge { // 覆写方法,可覆写的类方法见下文 } const Graph = extend(BaseGraph, { // 注册自定义边 edges: { 'custom-edge': CustomEdge, }, }); const graph = new Graph({ // ... 其他配置 edge: { type: 'custom-edge', // 使用注册的节点 }, }); ``` ## 覆写方法 ### draw :::info{title=提示} 大多数情况下并不需要覆写 draw 方法,更常用的做法是覆写 `drawKeyShape`、`drawLabelShape` 等方法,这些方法将在下文介绍。 ::: G5 5.0 移除了 `update` 和 `afterUpdate` 方法。现在只需要复写 `draw` 方法和 `afterDraw` 方法,G6 将自动根据更新的属性增量更新图形。 draw 方法通过调用 `this.drawKeyShape` 等方法分别绘制边各部分。 你可以参考 `line-edge` 类型边的 [draw](https://github.com/antvis/G6/blob/6be8f9810ec3b9310371f37de1a2591f14db67f1/packages/g6/src/stdlib/item/edge/line.ts#L28) 方法进行覆写。 ### afterDraw 在 `draw` 函数完成之后执行的逻辑,也可以用于绘制更多的图形,返回值和 `draw` 方法一致。在内置的节点类型中,没有对它进行实现。 ### drawKeyShape 绘制主图形(`keyShape`),该图形是必须的,例如直线边的主图形是一条直线(`line`) 以及首尾箭头(`arrow`),曲线边的主图形则将直线换成了曲线路径(`path`)。 覆写 `drawKeyShape` 方法绘制**直线边**的示例如下: ```typescript public drawKeyShape( model: EdgeDisplayModel, sourcePoint: Point, targetPoint: Point, shapeMap: EdgeShapeMap, ) { const { keyShape: keyShapeStyle } = this.mergedStyles; const { startArrow, endArrow, ...others } = keyShapeStyle; const lineStyle = { ...others, x1: sourcePoint.x, y1: sourcePoint.y, z1: sourcePoint.z || 0, x2: targetPoint.x, y2: targetPoint.y, z2: targetPoint.z || 0, isBillboard: true, }; // 绘制箭头 this.upsertArrow('start', startArrow, others, model, lineStyle); this.upsertArrow('end', endArrow, others, model, lineStyle); // 绘制并返回图形 return this.upsertShape('line', 'keyShape', lineStyle, shapeMap, model); } ``` 若要绘制曲线或折线,`drawKeyShape` 中应当根据控制点,计算路径。例如内置的 `quadratic-edge` 的 `drawKeyShape` 方法: ```typescript public drawKeyShape( model: EdgeDisplayModel, sourcePoint: Point, targetPoint: Point, shapeMap: EdgeShapeMap, ) { const { keyShape: keyShapeStyle } = this.mergedStyles as any; const { startArrow, endArrow, ...others } = keyShapeStyle; // 根据弧度位置、弧度等信息计算控制点 const controlPoint = this.getControlPoints( sourcePoint, targetPoint, keyShapeStyle.curvePosition, keyShapeStyle.controlPoints, keyShapeStyle.curveOffset, )[0]; const lineStyle = { ...others, path: [ ['M', sourcePoint.x, sourcePoint.y], ['Q', controlPoint.x, controlPoint.y, targetPoint.x, targetPoint.y], ], }; // 绘制箭头 this.upsertArrow('start', startArrow, others, model, lineStyle); this.upsertArrow('end', endArrow, others, model, lineStyle); // 绘制并返回图形 return this.upsertShape('path', 'keyShape', lineStyle, shapeMap, model); } ``` 其中,`this.getControlPoints` 可以进行复写,从而自定义控制点计算逻辑,见 [getControlPoints](#getcontrolpoints)。 ### drawHaloShape 绘制主图形轮廓图形(`haloShape`),通常在 `selected`, `active` 状态下显示。 若需要覆写,则可以参考 [BaseEdge.drawHaloShape](https://github.com/antvis/G6/blob/6be8f9810ec3b9310371f37de1a2591f14db67f1/packages/g6/src/stdlib/item/edge/base.ts#L464) ### drawLabelShape 绘制文本图形(`labelShape`) 若需要覆写,则可以参考 [BaseEdge.drawLabelShape](https://github.com/antvis/G6/blob/6be8f9810ec3b9310371f37de1a2591f14db67f1/packages/g6/src/stdlib/item/edge/base.ts#L194) ### drawLabelBackgroundShape 绘制文本图形的背景框图形(`labelBackgroundShape`) 若需要覆写,则可以参考 [BaseEdge.drawLabelBackgroundShape](https://github.com/antvis/G6/blob/6be8f9810ec3b9310371f37de1a2591f14db67f1/packages/g6/src/stdlib/item/edge/base.ts#L311) ### drawOtherShapes 绘制上述内容之外的图形,可以在 `drawOtherShapes` 中完成,例如额外创建一个圆形: ```typescript public drawOtherShapes( model: EdgeDisplayModel, shapeMap: EdgeShapeMap, ) { return { extraShape: upsertShape( 'circle', 'other-circle-shape', { r: 4, fill: '#0f0', x: -20, y: 0, }, shapeMap, ), }; } ``` ## 成员属性及方法 ### getControlPoints 获取控制点,通常用于计算路径。例如折线边的控制点是拐点,曲线边的控制点是曲线的控制点。 当继承 Extensions.PolylineEdge、Extensions.QuadraticEdge、Extensions.CubicEdge、Extensions.CubicHorizontalEdge、Extensions.CubicVerticalEdge 时,可以通过复写 `getControlPoints` 来修改控制点的逻辑。 `Extensions.PolylineEdge` 的 `getControlPoints` 类型为: ```typescript ( /** 边的渲染数据 */ model: EdgeDisplayModel, /** 边的起点 */ sourcePoint: Point, /** 边的终点 */ targetPoint: Point, ) => /** 计算后的控制点 */ { x: number; y: number; z?: number; }[] ``` `Extensions.QuadraticEdge`、`Extensions.CubicEdge`、`Extensions.CubicHorizontalEdge`、`Extensions.CubicVerticalEdge` 的 `getControlPoints` 类型为: ```typescript ( /** 边的起点 */ startPoint: Point, /** 边的终点 */ endPoint: Point, /** 控制点的投影在两端点连线上的百分比,范围 0 到 1 */ percent: number, /** 数据中控制点配置 */ controlPoints: Point[], /** 弧度距离 */ offset: number, ) => /** 计算后的控制点 */ { x: number; y: number; z?: number; }[]; ``` ### getPath `Extensions.PolylineEdge` 的成员方法,仅在继承它来实现自定义边时可复写。由于折线的自动寻径算法比较复杂,因此单独抽出了这个函数。也由于算法复杂性,折线边的性能稍差。如果有确定的折线边绘制规则,可以通过继承内置折线边,自定义 `getPath` 方法覆盖自动寻径的逻辑。函数类型为: ```typescript ( /** 边的渲染数据 */ model: EdgeDisplayModel, /** 起点和终点 */ points: Point[], /** 折线拐点的弧度 */ radius: number, /** 折线弯折的配置 */ routeCfg?: RouterCfg, /** 是否使用 A* 算法 */ auto?: boolean, ) => /** 路径 */ string; ```
RouterCfg ```ts type RouterCfg = { name: 'orth' | 'er'; /** 线与点之间的间距 */ offset?: number; /** 网格大小 */ gridSize?: number; /** 最大旋转角度(弧度) */ maxAllowedDirectionChange?: number; /** 允许的边的方向 */ directions?: any[]; /** 起点和终点的权重 */ penalties?: {}; /** 是否使用简单的折线拐点寻径算法 */ simple?: boolean; /** 计算两点之间距离的函数 */ distFunc?: (p1: PolyPoint, p2: PolyPoint) => number; /** 简化的寻径函数 */ fallbackRoute?: (p1: PolyPoint, p2: PolyPoint, startNode?: Node, endNode?: Node, cfg?: RouterCfg) => PolyPoint[]; /** 最大循环次数 */ maximumLoops?: number; /** 是否自动避开障碍物,默认为 false */ enableObstacleAvoidance?: boolean; }; ```