G6(九)进阶:自定义边

G6 提供了自定义边的机制,方便更加定制化的边,包括含有复杂图形的边、复杂交互的边、带有动画的边等。

可以通过 G6.registerEdge(typeName: string, edgeDefinition: object, extendedEdgeType?: string) 注册一个新的边类型,其中:

  • typeName:该新边类型名称;
  • extendedEdgeType:被继承的边类型,可以是内置边类型名,也可以是其他自定义边的类型名。extendedEdgeType 未指定时代表不继承其他类型的边;
  • edgeDefinition:该新边类型的定义,其中必要函数详见 自定义机制 API。当有 extendedEdgeType 时,没被复写的函数将会继承 extendedEdgeType 的定义。

需要注意的是,自定义边/节点时,若给定了 extendedEdgeType,如 draw,update,setState 等必要的函数若不在 edgeDefinition 中进行复写,将会继承 extendedEdgeType 中的相关定义。

定义边

实现垂直的折线:

自定义边

js 复制代码
G6.registerEdge('hvh', {
  draw(cfg, group) {
    const startPoint = cfg.startPoint;
    const endPoint = cfg.endPoint;
    const shape = group.addShape('path', {
      attrs: {
        stroke: '#333',
        path: [
          ['M', startPoint.x, startPoint.y],
          ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处
          ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处
          ['L', endPoint.x, endPoint.y],
        ],
      },
      // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
      name: 'path-shape',
    });
    return shape;
  },
});
  • 上面自定义边中的 startPoint 和 endPoint 分别是是边两端与起始节点和结束节点的交点;
  • 可以通过修改节点的锚点(边连入点)来改变 startPoint 和 endPoint 的位置。

在数据中修改 anchorPoints

通过以下的数据,使用自定义的 hvh 边,就可以实现上图最右边的效果。

js 复制代码
const data = {
  nodes: [
    {
      id: 'node1',
      x: 100,
      y: 200,
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
    },
    {
      id: 'node2',
      x: 200,
      y: 100,
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
    },
    {
      id: 'node3',
      x: 200,
      y: 300,
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
    },
  ],
  edges: [
    {
      id: 'edge1',
      target: 'node2',
      source: 'node1',
      type: 'hvh',
    },
    {
      id: 'edge2',
      target: 'node3',
      source: 'node1',
      type: 'hvh',
    },
  ],
};

扩展现有边

通过 afterDraw 接口给现有的曲线增加动画。

js 复制代码
G6.registerEdge(
  'line-growth',
  {
    afterDraw(cfg, group) {
      const shape = group.get('children')[0];
      const length = shape.getTotalLength();
      shape.animate(
        (ratio) => {
          const startLen = ratio * length;
          const cfg = {
            lineDash: [startLen, length - startLen],
          };
          return cfg;
        },
        {
          repeat: true,
          duration: 2000,
        },
      );
    },
  },
  'cubic',
);

增加额外图形

通过实现 afterDraw 增加额外图形,为找到边的主要图形 path 上的某个点,可以使用 shape.getPoint(ratio) 获得。

js 复制代码
G6.registerEdge(
  'mid-point-edge',
  {
    afterDraw(cfg, group) {
      // 获取图形组中的第一个图形,在这里就是边的路径图形
      const shape = group.get('children')[0];
      // 获取路径图形的中点坐标
      const midPoint = shape.getPoint(0.5);
      // 在中点增加一个矩形,注意矩形的原点在其左上角
      group.addShape('rect', {
        attrs: {
          width: 10,
          height: 10,
          fill: '#f00',
          // x 和 y 分别减去 width / 2 与 height / 2,使矩形中心在 midPoint 上
          x: midPoint.x - 5,
          y: midPoint.y - 5,
        },
        name: 'mid-point-edge-rect', // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
      });
    },
    update: undefined,
  },
  'cubic',
);

边的交互样式

以点击选中、鼠标 hover 到边为示例,实现如下效果:

边过细时点击很难被击中,可以设置 lineAppendWidth 来提升击中范围。

js 复制代码
// 基于 line 扩展出新的边
G6.registerEdge(
  'custom-edge',
  {
    // 响应状态变化
    setState(name, value, item) {
      const group = item.getContainer();
      const shape = group.get('children')[0]; // 顺序根据 draw 时确定
      if (name === 'active') {
        if (value) {
          shape.attr('stroke', 'red');
        } else {
          shape.attr('stroke', '#333');
        }
      }
      if (name === 'selected') {
        if (value) {
          shape.attr('lineWidth', 3);
        } else {
          shape.attr('lineWidth', 2);
        }
      }
    },
  },
  'line',
);

// 点击时选中,再点击时取消
graph.on('edge:click', (ev) => {
  const edge = ev.item;
  graph.setItemState(edge, 'selected', !edge.hasState('selected')); // 切换选中
});

graph.on('edge:mouseenter', (ev) => {
  const edge = ev.item;
  graph.setItemState(edge, 'active', true);
});

graph.on('edge:mouseleave', (ev) => {
  const edge = ev.item;
  graph.setItemState(edge, 'active', false);
});
相关推荐
桃园码工2 天前
4_使用 HTML5 Canvas API (3) --[HTML5 API 学习之旅]
前端·html5·canvas
桃园码工8 天前
1_HTML5 Canvas 概述 --[HTML5 API 学习之旅]
前端·html5·canvas
码上佳人16 天前
Uniapp中canvas画图生成图片并下载到相册
uni-app·canvas
普兰店拉马努金19 天前
【Canvas与图标】乡土风金属铝边立方红黄底黑字图像处理图标
canvas·图标
SunFlower91421 天前
v3通过pdfjs-dist插件渲染后端返回的pdf文件流,并实现缩放、下一页
前端·svg·canvas·pdfjs-dist
普兰店拉马努金23 天前
【Canvas与雷达】点鼠标可暂停金边蓝屏雷达显示屏
canvas·雷达
Marshall35721 个月前
Canvas 和 SVG 的高级使用与性能优化
前端·svg·canvas
webmote1 个月前
做一个FabricJS.cc的中文文档网站——面向markdown编程
canvas·fabric·使用手册·中文·fabricjs
小黄人软件1 个月前
【AI协作】让所有用电脑的场景都能在ChatGPT里完成。Canvas :新一代可视化交互,让AI易用易得
人工智能·chatgpt·canvas
柳晓黑胡椒1 个月前
cesiusm实现 多图例展示+点聚合(base64图标)
css3·canvas·base64·cesium·animation