antvis/G6

关于自定义节点,点击节点高亮的疑问 #6013

ltdys posted onGitHub

Describe the bug / 问题描述

版本:"@antv/g6": "^4.8.24", 需求: 点击节点,canvas出现遮罩层,只凸显节点相邻的下游和上游全链路(包括节点、边,箭头),点击其它节点,之前点击的恢复初始化样式,被点击的高亮。节点是自定义节点。 实现:点击节点后,通过节点寻找到节点相邻的下游和上游全链路,循环所有的节点,不在这些链路之路的,改变背景色,添加透明度,来实现模拟的遮罩效果 问题:1、使用graph.clearItemStates(node)或graph.clearItemStates(edge)后,拖拽或缩放会出现绘制多个节点的情况 2、点击节点改变节点的样式,再次点击无法恢复回来。试了使用update或者updateItem都没成功。

No response

Steps to Reproduce the Bug or Issue / 重现步骤

// 高亮边(祖先节点) this.graph.on('node:click', (e) => { if (e.target.get('name') === 'collapse-icon') return const item = e.item; const target = e.target const model = item.getModel()

    this.clearAllStats()

    const currentNodes = this.graph.findById(model.id)
    const neighborNodes = currentNodes.getNeighbors()

    const currentEdges = this.graph.findById(model.id).getEdges()
    let allEdges = []
    let allNodes = []

    this.graph.setAutoPaint(false);

    // 获取所有的父节点(包含祖先节点)
    const parentIds = this.findAllAncestors(this.mockData.children, model.id)
    let parentEdges = []
    let parentNodes = []
    for (let parentId of parentIds) {
      const findNodes = this.graph.findById(parentId)
      const findEdges = findNodes.getEdges()[0]
      parentNodes.push(findNodes)
      parentEdges.push(findEdges)
    }
    // 所有的边
    allEdges = [...parentEdges, ...currentEdges]
    allEdges.forEach(connectedNode => {
      this.graph.setItemState(connectedNode, 'highlight', true);
    })
    this.graph.getEdges().forEach(edge => {
      if (!allEdges.includes(edge)) {
        this.graph.setItemState(edge, 'inactive', true);
      }
    })

    allNodes = [...[currentNodes], ...neighborNodes, ...parentNodes]
    this.graph.getNodes().forEach(node => {
      const getrect = node.getKeyShape()
      const updateNodeId = node.getModel().id
      const updateNode = this.graph.findById(updateNodeId)
     // 这里要改变节点的样式,这样是可以成功的。但是点击其它的节点后,不知道怎么恢复回来
      if (!allNodes.includes(node)) {
        getrect.attr({
           fill: 'rgba(0, 0, 0, 0.5)',
           stroke: '#000',
           filter: 'blur(5px)',
           opacity: 0.2
         })
      }
    this.graph.paint(); // 触发重绘  
    this.graph.setAutoPaint(true); // 恢复自动重绘
  });

// 自定义节点的代码 G6.registerNode('flow-rect', { shapeType: 'flow-rect', draw: function drawShape(cfg, group) { let { name = '', beforeRatio, afterRatio, tipInfo, nodeStyleName, inactive } = cfg; nodeStyleName = nodeStyleName || 'nodeStyle1' if (inactive) nodeStyleName = 'nodeStyleInactive' const useNodeStyle = nodeStyle[nodeStyleName]

      let w = 253;
      if (name === '资产分发指标') w = 180
      let h = 44;  // 44 68 104
      if (beforeRatio) h = 68
      if (beforeRatio && tipInfo) h = 104
      const rectConfig = {
        width: w,
        height: h,
        lineWidth: 1,
        fontSize: 12,
        fill: useNodeStyle.bgColor,  // 背景色
        stroke: useNodeStyle.borderColor,  // 边框色
        radius: [0, 8, 8, 0],
        opacity: 1,
      };
      const nodeOrigin = {
        x: -rectConfig.width / 2,
        y: -rectConfig.height / 2,
      };
      const textConfig = {
        textAlign: 'left',
        textBaseline: 'bottom',
      };

      const rect = group.addShape('rect', {
        attrs: {
          x: nodeOrigin.x,
          y: nodeOrigin.y,
          ...rectConfig,
        },
      });

      const rectBBox = rect.getBBox();

      // name
      const nameText = group.addShape('text', {
        attrs: {
          ...textConfig,
          x: nodeOrigin.x + 12,
          y: nodeOrigin.y + 20 + 12,  // 20行高
          text: name.length > 28 ? name.substr(0, 28) + '...' : name,
          fontSize: 14,
          fontWeight: '600',
          opacity: 1,
          fill: '#333333',
          cursor: 'pointer',
          fontFamily: 'PingFangSC, PingFang SC'
        },
        name: 'name-shape',
      });

      if (beforeRatio) {
        // 占比前
        const beforeRatioText = group.addShape('text', {
          attrs: {
            ...textConfig,
            x: nodeOrigin.x + 12,
            y: nameText.getBBox().maxY + 26,
            text: beforeRatio,
            fontSize: 18,
            fontWeight: 'bold',
            fill: '#121736',
            opacity: 1,
            fontFamily: 'QiFu-Sans-Std, QiFu-Sans-Std'
          },
        })

        // 前一天,后一天
        const dateText = group.addShape('text', {
          attrs: {
            ...textConfig,
            x: beforeRatioText.getBBox().maxX + 4,
            y: nameText.getBBox().maxY + 23,
            text: this.getDateText(),
            fontSize: 12,
            fontWeight: 600,
            fill: '#121736',
            opacity: 1,
            fontFamily: 'PingFangSC, PingFang SC'
          },
        });

        group.addShape('image', {
          attrs: {
            x: dateText.getBBox().maxX + 2,
            y: nameText.getBBox().maxY + 12,
            width: 9,
            height: 10,
            img: downImageData,
          },
          name: 'image-shape',
        });

        // 占比后
        group.addShape('text', {
          attrs: {
            ...textConfig,
            x: dateText.getBBox().maxX + 7 + 5,
            y: nameText.getBBox().maxY + 24,
            text: afterRatio,
            fontSize: 12,
            fill: '#FF0000',
            opacity: 1,
          },
        });
      }

      if (tipInfo) {
        // tip背景色
        group.addShape('rect', {
          attrs: {
            width: this.getTextSize(tipInfo, 12) + 32,
            height: 30,
            x: nodeOrigin.x + 12,
            y: rectBBox.maxY - 40,
            fill: '#ffffff',
            radius: 6,
          },
          name: 'rect-shape-tiptext'
        })

        // icon
        group.addShape('image', {
          attrs: {
            x: nodeOrigin.x + 20,
            y: rectBBox.maxY - 32,
            width: 10,
            height: 10,
            img: warningImageData,
          },
          name: 'image-shape-warning',
        });

        // tip
        group.addShape('text', {
          attrs: {
            ...textConfig,
            x: nodeOrigin.x + 34,
            y: rectBBox.maxY - 20,
            text: tipInfo,
            fontSize: 12,
            fill: 'red',
            stroke: '#ffffff',
            opacity: 1,
          },
        })
      }

      // left border
      const leftRect = group.addShape('rect', {
        attrs: {
          x: nodeOrigin.x,
          y: nodeOrigin.y,
          width: 2,
          height: rectBBox.height,
          radius: 0,
          fill: useNodeStyle.borderLeftColor,
        },
      });

      cfg.children &&
        group.addShape('marker', {
          attrs: {
            x: w / 2,
            y: 0,
            r: 6,
            cursor: 'pointer',
            symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
            stroke: '#fff',
            lineWidth: 1,
            fill: '#333333',
          },
          // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
          name: 'collapse-icon',
          modelId: cfg.name,
        });

      return rect;
    },

    setState(name, value, item) {
      if (name === 'collapsed') {
        const marker = item.get('group').find((ele) => ele.get('name') === 'collapse-icon');
        const icon = value ? G6.Marker.expand : G6.Marker.collapse;
        marker.attr('symbol', icon);
      }
    },

    getDateText() {
      let result = ''
      switch (_dateType) {
        case 'day':
          result = '比前一天'
          break;
        case 'week':
          result = '比前一周'
          break;
        case 'month':
          result = '比前一月'
          break;
      }
      return result
    },

    getTextSize(text, fontSize) {
      const span = document.createElement('span')
      const result = {}
      result.width = span.offsetWidth
      result.height = span.offsetWidth
      span.style.visibility = 'hidden'
      span.style.fontSize = fontSize + 'px'
      document.body.appendChild(span)
      if (typeof span.textContent !== 'undefined') {
        span.textContent = text
      } else {
        span.innerText = text
      }
      result.width = span.offsetWidth - result.width
      result.height = span.offsetHeight - result.height
      span.parentNode.removeChild(span)
      return result.width
    },
  });

// 清空所有节点和边 clearAllStats() { this.graph.setAutoPaint(false); this.graph.getNodes().forEach((node) => { this.graph.clearItemStates(node); }); this.graph.getEdges().forEach((edge) => { this.graph.clearItemStates(edge); }); this.graph.paint(); this.graph.setAutoPaint(true); },

G6 Version / G6 版本

4.x

Operating System / 操作系统

Windows

Browser / 浏览器

Chrome

Additional context / 补充说明

求大佬指教,或者找个demo让我看下也好,卡住了不知道该怎么改。


@ltdys 你好,最后是怎么解决的?

posted by scarqin 8 months ago

Fund this Issue

$0.00
Funded

Pull requests