前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@antv/g6自定义节点dom类型shape无法触发事件原因分析

@antv/g6自定义节点dom类型shape无法触发事件原因分析

作者头像
路过君
发布2023-05-27 14:31:08
1.5K0
发布2023-05-27 14:31:08
举报

版本

@antv/g6: 4.8.10

踩坑

根据官网文档注册自定义节点时如果绘制dom类型shape,会发现node相关事件全都无法触发,比如node:click等 例如:

代码语言:javascript
复制
G6.registerNode('my-dom-node', {
  draw(cfg, group) {
    const keyShape = group.addShape('dom', {
      attrs: {
        html:'<h1>hello</h1>'
      },
      name: 'dom-shape',
      draggable: true,
    });
    return keyShape;
  }
});

原因分析

g6只有svg渲染模式画布可以支持dom类型节点,原理是通过foreignObject标签渲染dom 在事件触发时canvas会对比svg dom拾取的对象和shape对应的标签确定触发哪个节点的事件 @antv/g-svg/src/canvas.ts

代码语言:javascript
复制
// 覆盖 Container 中通过遍历的方式获取 shape 对象的逻辑,直接走 SVG 的 dom 拾取即可
getShape(x: number, y: number, ev: Event): IShape {
  let target = <Element>ev.target || <Element>ev.srcElement;
  // 
  if (!SHAPE_TO_TAGS[target.tagName]) {
    let parent = <Element>target.parentNode;
    while (parent && !SHAPE_TO_TAGS[parent.tagName]) {
      parent = <Element>parent.parentNode;
    }
    target = parent;
  }
  return this.find((child) => child.get('el') === target) as IShape;
}

此逻辑中通过SHAPE_TO_TAGS的映射判断dom是否对应到shape的逻辑有问题,SHAPE_TO_TAGS值如下,可见映射关系反了,导致并不能通过foreignObject标签获取到类型dom,从而无法正确定位dom类型的shape

代码语言:javascript
复制
circle: "circle"
dom: "foreignObject"
ellipse: "ellipse"
image: "image"
line: "line"
marker: "path"
path: "path"
polygon: "polygon"
polyline: "polyline"
rect: "path"
text: "text"

在event-controller中只有当通过getShape拾取到shape时才会触发节点事件 @antv/g-base/src/event/event-controller.ts

代码语言:javascript
复制
 // 触发事件
  _triggerEvent(type, ev) {
    const pointInfo = this._getPointInfo(ev);
    // 每次都获取图形有一定成本,后期可以考虑进行缓存策略
    const shape = this._getShape(pointInfo, ev);
    const method = this[`_on${type}`];
    let leaveCanvas = false;
    if (method) {
      method.call(this, pointInfo, shape, ev);
    } else {
      const preShape = this.currentShape;
      // 如果进入、移出画布时存在图形,则要分别触发事件
      if (type === 'mouseenter' || type === 'dragenter' || type === 'mouseover') {
        this._emitEvent(type, ev, pointInfo, null, null, shape); // 先进入画布
        if (shape) {
          this._emitEvent(type, ev, pointInfo, shape, null, shape); // 再触发图形的事件
        }
        if (type === 'mouseenter' && this.draggingShape) {
          // 如果正在拖拽图形, 则触发 dragleave
          this._emitEvent('dragenter', ev, pointInfo, null);
        }
      } else if (type === 'mouseleave' || type === 'dragleave' || type === 'mouseout') {
        leaveCanvas = true;
        if (preShape) {
          this._emitEvent(type, ev, pointInfo, preShape, preShape, null); // 先触发图形的事件
        }
        this._emitEvent(type, ev, pointInfo, null, preShape, null); // 再触发离开画布事件
        if (type === 'mouseleave' && this.draggingShape) {
          this._emitEvent('dragleave', ev, pointInfo, null);
        }
      } else {
        this._emitEvent(type, ev, pointInfo, shape, null, null); // 一般事件中不需要考虑 from, to
      }
    }
    if (!leaveCanvas) {
      this.currentShape = shape;
    }
    // 当鼠标从画布移动到 shape 或者从 preShape 移动到 shape 时,应用 shape 上的鼠标样式
    if (shape && !shape.get('destroyed')) {
      const canvas = this.canvas;
      const el = canvas.get('el');
      el.style.cursor = shape.attr('cursor') || canvas.get('cursor');
    }
  }

另一个坑

自定义节点时最好覆盖drawShape方法而不是draw方法

源码分析

通过分析shapeBase源码可知,draw方法通过调用drawShap方法获取shape对象,并注册到shapeMap映射中,如果直接覆盖draw则导致无法正确映射 此外draw还额外增加了label的绘制

代码语言:javascript
复制
 /**
   * 绘制节点/边,包含文本
   * @override
   * @param  {Object} cfg 节点的配置项
   * @param  {G.Group} group 节点的容器
   * @return {IShape} 绘制的图形
   */
  draw: function draw(cfg, group) {
    group['shapeMap'] = {};
    this.mergeStyle = this.getOptions(cfg);
    // 获取要绘制的图形
    var shape = this.drawShape(cfg, group);
    // 设置样式
    shape.set('className', this.itemType + CLS_SHAPE_SUFFIX);
    // 添加映射关系
    group['shapeMap'][this.itemType + CLS_SHAPE_SUFFIX] = shape;
    if (cfg.label) {
      var label = this.drawLabel(cfg, group);
      label.set('className', this.itemType + CLS_LABEL_SUFFIX);
      group['shapeMap'][this.itemType + CLS_LABEL_SUFFIX] = label;
    }
    return shape;
  },
  drawShape: function drawShape(cfg, group) {
    // 默认没有绘制图形
    return null; 
  },
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 版本
  • 踩坑
  • 原因分析
  • 另一个坑
  • 源码分析
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档