Antv G6 force分布式布局 icon“+“ “-“收缩自定义,关系图子节点

子节点收缩

javascript 复制代码
    const collapseIcon = (x, y, r) => {
       // 折叠
       return [
         ['M', x - r, y],
         ['a', r, r, 0, 1, 0, r * 2, 0],
         ['a', r, r, 0, 1, 0, -r * 2, 0],
         ['M', x - r + 4, y],
         ['L', x - r + 2 * r - 4, y]
       ]
     }
     const expandIcon = (x, y, r) => {
       // 拓展
       return [
         ['M', x - r, y],
         ['a', r, r, 0, 1, 0, r * 2, 0],
         ['a', r, r, 0, 1, 0, -r * 2, 0],
         ['M', x - r + 4, y],
         ['L', x - r + 2 * r - 4, y],
         ['M', x - r + r, y - r + 4],
         ['L', x, y + r - 4]
       ]
     }
draw(cfg, group) {
      const { noSubsetSty, collapsed } = cfg
      if (noSubsetSty) { // 需要折叠的节点标志,在数据中处理添加标记
        group.addShape('circle', {
          attrs: {
            x: size,
            y: 0,
            r: 13,
            fill: 'rgba(47, 84, 235, 0.15)',
            opacity: 0,
            zIndex: -2
          },
          name: 'collapse-icon-bg'
        })
        group.addShape('marker', {
          attrs: {
            x: size,
            y: 0,
            r: 7,
            symbol: collapsed ? collapseIcon : expandIcon,
            stroke: 'rgba(0,0,0,1)',
            fill: 'rgba(0,0,0,0)',
            lineWidth: 1,
            cursor: 'pointer'
          },
          name: 'collapse-icon'
        })
      }
    },
    afterDraw(cfg, group) {
      const icon = group.find(
        element => element.get('name') === 'collapse-icon'
      )
      if (icon) {
        const bg = group.find(
          element => element.get('name') === 'collapse-icon-bg'
        )
        icon.on('mouseenter', () => {
          bg.attr('opacity', 1)
        })
        icon.on('mouseleave', () => {
          bg.attr('opacity', 0)
        })
      }
    },
    setState(name, value, item) {
      const group = item.getContainer()
      if (group.get('children')) {
        if (name === 'collapsed') {
          const marker = item
            .get('group')
            .findAll(ele => ele.get('name') === 'collapse-icon')
          marker[0] &&
            marker[0].attr('symbol', value ? collapseIcon : expandIcon)
          if (item._cfg.model.depth === 0) {
            marker[1] &&
              marker[1].attr('symbol', value ? collapseIcon : expandIcon)
          }
        }
      }
    }
javascript 复制代码
 this.graph.on('node:click', e => {
        // 处理子节点展示问题 + -
        if (e.target.get('name') === 'collapse-icon') {
          const item = e.item
          const nodeId = item.get('id')
          const children = selectedWorks(nodeId, regroup.nodes, regroup.edges) // 查询相关子节点
          let newNodes = children.nodes
          if (newNodes.length) {
            if (e.item._cfg.model.collapsed) {
              for (let i = 0; i < newNodes.length; i++) {
                this.hiddenElements.push(newNodes[i].id)
                const item = this.graph.findById(newNodes[i].id)
                this.graph.hideItem(item, true)
                this.graph.paint()
              }
            } else {
              for (let i = 0; i < newNodes.length; i++) {
                this.hiddenElements.splice(
                  this.hiddenElements.indexOf(newNodes[i].id),
                  1
                )
                const item = this.graph.findById(newNodes[i].id)
                this.graph.showItem(item, true)
                this.graph.paint()
              }
            }
          }
          this.graph.layout({
            type: 'force',
            nodeSize: 50,
            preventOverlap: true,
            animate: false,
            linkDistance: d => {
              return 80
            },
            nodeStrength: -150,
            onTick: () => {
              this.graph.refreshPositions()
            },
            nodeSpacing: d => {
              return 80
            }
          })
          e.item._cfg.model.collapsed = !e.item._cfg.model.collapsed
          this.graph.setItemState(
            e.item,
            'collapsed',
            e.item._cfg.model.collapsed
          )
          this.graph.paint()
        }
      })

节点查找方法

javascript 复制代码
/**
 * 只有被指向的点并且没有指向的点  找最末端的点,并且被指向的方向只有一个
 * source ---->  target
 */
export function noSubsetHandle(nodeID, edges) {
  let idT = edges.map(i => i.target)
  let idS = edges.map(i => i.source)
  Array.prototype.countCertainElements = function(value) {
    return this.reduce((sum, element) => (element == value ? sum + 1 : sum), 0)
  }
  let index = idT.countCertainElements(nodeID)
  let results =
    idT.indexOf(nodeID + '') > -1 &&
    index === 1 &&
    idS.indexOf(nodeID + '') === -1
  return results
}
/**
 * @description 依据节点ID 查询 子集 和 相关 边
 * @param {*} nodeID
 * @param {*} nodes
 * @param {*} edges
 */
export function selectedWorks(nodeID, nodes, edges) {
  let edgesV = []
  let nodesV = []
  edges.map(e => {
    if (+e.source === +nodeID) {
      edgesV.push({ ...e })
    }
  })
  if (edgesV.length > 0) {
    nodes.map(e => {
      if (edgesV.findIndex(x => +x.target === +e.id) > -1) {
        if (noSubsetHandle(+e.id, edges)) {
          nodesV.push({ ...e })
        }
      }
    })
  }
  let edgesVv= []
  edgesV.length && edgesV.map(i => {
    if (nodesV.findIndex(x => +x.id === +i.target) > -1) {
      edgesVv.push({...i})
    }
  })
  let obj = {}
  obj.nodes = nodesV
  obj.edges = edgesVv
  return obj
}
相关推荐
hpoenixf3 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特3 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷3 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
Liu628883 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
AI科技星4 小时前
全尺度角速度统一:基于 v ≡ c 的纯推导与验证
c语言·开发语言·人工智能·opencv·算法·机器学习·数据挖掘
mengchanmian4 小时前
前端node常用配置
前端
深蓝电商API4 小时前
分布式事务在跨境交易中的解决方案
分布式·跨境电商·代购系统·反向海淘·代购平台·跨境代购
华洛4 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq4 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
条tiao条4 小时前
KMP 算法详解:告别暴力匹配,让字符串匹配 “永不回头”
开发语言·算法