G6 icon indicating copy to clipboard operation
G6 copied to clipboard

力导向图在vue中,碰撞检测问题

Open ligouhai opened this issue 2 years ago • 10 comments

问题描述

vue版本2.6.1 @antv/g6版本4.3.11 原封不动复制官网的例子,加入到vue的mounted生命周期,加进去后一个是没有动画,于是手动加了个animate:true 但碰撞检测和重叠都有点问题 链接里用的vue3,不过这不重要,vue3,vue2,antv/g6 4.3 4.5都会有问题,是我语法用的有问题还是怎样 这是官网的实例连接 https://g6.antv.vision/zh/examples/net/forceDirected#forceBubbles

<template>
  <div id="test" />
</template>


<script>
import G6 from '@antv/g6'
export default {
  mounted() {
    function _defineProperty(obj, key, value) {
      if (key in obj) {
        Object.defineProperty(obj, key, {
          value,
          enumerable: true,
          configurable: true,
          writable: true
        })
      } else {
        obj[key] = value
      }
      return obj
    }

    const colors = [
      '#BDD2FD',
      '#BDEFDB',
      '#C2C8D5',
      '#FBE5A2',
      '#F6C3B7',
      '#B6E3F5',
      '#D3C6EA',
      '#FFD8B8',
      '#AAD8D8',
      '#FFD6E7'
    ]
    const strokes = [
      '#5B8FF9',
      '#5AD8A6',
      '#5D7092',
      '#F6BD16',
      '#E8684A',
      '#6DC8EC',
      '#9270CA',
      '#FF9D4D',
      '#269A99',
      '#FF99C3'
    ]

    const data = {
      nodes: [
        {
          id: '0',
          label: '0',
          value: 10,
          cluster: 'a',
          description: 'this is node 0, \nand the value of it is 10'
        },
        {
          id: '1',
          label: '1',
          value: 20,
          cluster: 'b',
          description: 'this is node 1, \nand the value of it is 20'
        },
        {
          id: '2',
          label: '2',
          value: 5,
          cluster: 'a',
          description: 'this is node 2, \nand the value of it is 5'
        },
        {
          id: '3',
          label: '3',
          value: 10,
          cluster: 'a',
          description: 'this is node 3, \nand the value of it is 10'
        },
        {
          id: '4',
          label: '4',
          value: 12,
          cluster: 'c',
          subCluster: 'sb',
          description: 'this is node 4, \nand the value of it is 12'
        },
        {
          id: '5',
          label: '5',
          value: 18,
          cluster: 'c',
          subCluster: 'sa',
          description: 'this is node 5, \nand the value of it is 18'
        },
        {
          id: '6',
          label: '6',
          value: 3,
          cluster: 'c',
          subCluster: 'sa',
          description: 'this is node 6, \nand the value of it is 3'
        },
        {
          id: '7',
          label: '7',
          value: 7,
          cluster: 'b',
          subCluster: 'sa',
          description: 'this is node 7, \nand the value of it is 7'
        },
        {
          id: '8',
          label: '8',
          value: 21,
          cluster: 'd',
          subCluster: 'sb',
          description: 'this is node 8, \nand the value of it is 21'
        },
        {
          id: '9',
          label: '9',
          value: 9,
          cluster: 'd',
          subCluster: 'sb',
          description: 'this is node 9, \nand the value of it is 9'
        }
      ],
      edges: []
    }

    const tipDiv = document.createElement('div')
    tipDiv.innerHTML = 'Try to click or drag a bubble!'
    const graphDiv = document.getElementById('test')
    graphDiv.appendChild(tipDiv)

    const container = document.getElementById('test')
    const width = container.scrollWidth
    const height = (container.scrollHeight || 500) - 20

    const graph = new G6.Graph({
      container: 'test',
      width,
      height,
      animate: true,
      layout: {
        type: 'force',
        nodeStrength: 30,
        collideStrength: 0.7,
        linkDistance: 50,
        alphaDecay: 0.01,
        preventOverlap: true
      },
      modes: {
        default: ['drag-node']
      },
      defaultNode: {
        size: [10, 10]
      }
    })

    // mapping
    const nodes = data.nodes
    const nodeMap = new Map()
    const clusterMap = new Map()
    let clusterId = 0
    nodes.forEach((node) => {
      nodeMap.set(node.id, node)
      // cluster
      if (node.cluster && clusterMap.get(node.cluster) === undefined) {
        clusterMap.set(node.cluster, clusterId)
        clusterId++
      }
      const cid = clusterMap.get(node.cluster)
      if (!node.style) node.style = {}
      node.style.fill = colors[cid % colors.length]
      node.style.stroke = strokes[cid % strokes.length]
      node.x = width / 2 + 200 * (Math.random() - 0.5)
      node.y = height / 2 + 200 * (Math.random() - 0.5)
    })

    // map the value to node size
    let maxNodeValue = -9999
    let minNodeValue = 9999
    nodes.forEach(function(n) {
      if (maxNodeValue < n.value) maxNodeValue = n.value
      if (minNodeValue > n.value) minNodeValue = n.value
    })
    const nodeSizeRange = [10, 30]
    const nodeSizeDataRange = [minNodeValue, maxNodeValue]
    scaleNodeProp(nodes, 'size', 'value', nodeSizeDataRange, nodeSizeRange)

    nodes.forEach(function(node) {
      node.oriSize = node.size
      node.oriLabel = node.label
    })

    function refreshDragedNodePosition(e) {
      const model = e.item.get('model')
      model.fx = e.x
      model.fy = e.y
    }
    graph.on('node:dragstart', function(e) {
      graph.layout()
      refreshDragedNodePosition(e)
    })
    graph.on('node:drag', function(e) {
      refreshDragedNodePosition(e)
    })
    graph.on('node:dragend', function(e) {
      e.item.get('model').fx = null
      e.item.get('model').fy = null
    })
    graph.on('node:click', function(e) {
      const node = e.item
      const states = node.getStates()
      let clicked = false
      const model = node.getModel()
      let size = 200
      let labelText = 'NODE: ' + model.id + '\n' + model.description
      states.forEach(function(state) {
        if (state === 'click') {
          clicked = true
          size = model.oriSize
          labelText = model.oriLabel
        }
      })
      graph.setItemState(node, 'click', !clicked)
      graph.updateItem(node, {
        size,
        label: labelText
      })
      graph.layout()
    })

    graph.data(data)
    graph.render()

    if (typeof window !== 'undefined') {
      window.onresize = () => {
        if (!graph || graph.get('destroyed')) return
        if (!container || !container.scrollWidth || !container.scrollHeight) return
        graph.changeSize(container.scrollWidth, container.scrollHeight - 20)
      }
    }

    function scaleNodeProp(elements, propName, refPropName, dataRange, outRange) {
      const outLength = outRange[1] - outRange[0]
      const dataLength = dataRange[1] - dataRange[0]
      elements.forEach(function(n) {
        if (propName.split('.')[0] === 'style') {
          if (n.style) {
            n.style[propName.split('.')[1]] =
          ((n[refPropName] - dataRange[0]) * outLength) / dataLength + outRange[0]
          } else {
            n.style = _defineProperty(
              {},
              propName.split('.')[1],
              ((n[refPropName] - dataRange[0]) * outLength) / dataLength + outRange[0]
            )
          }
        } else {
          n[propName] = ((n[refPropName] - dataRange[0]) * outLength) / dataLength + outRange[0]
        }
      })
    }
  }
}
</script>

<style lang="scss" scoped>
#test {
  height: 800px;
}

</style>

重现链接

https://stackblitz.com/edit/vue-da2vvw?file=package.json,src%2FApp.vue

重现步骤

先是升级了最新版本不行,后面降级成4.3.11也不行,但单独新建html可以正常显示

预期行为

碰撞检测能正常,并且不会重叠

平台

  • 操作系统: [macOS, Windows, Linux, React Native ...]
  • 网页浏览器: [Google Chrome, Safari, Firefox]
  • G6 版本: [4.3.11 ]

屏幕截图或视频(可选)

image

补充说明(可选)

No response

ligouhai avatar Aug 26 '22 02:08 ligouhai

我似乎也是的 Vue3框架 仿着官方例子写的力导引demo,发现动画没有出现 type: 'force', 的demo效果像官方示例中 type: 'forceAtlas2' 的效果一样

ShineLuLu avatar Sep 02 '22 01:09 ShineLuLu

纯html里使用没啥问题 我后来实在没办法,在项目里引的iframe

ligouhai avatar Sep 02 '22 06:09 ligouhai

不用加 animate: true,在 layout 配置中加一行:onTick: () => graph.refreshPositions(), 相当于在布局过程中每一次迭代调用一次节点位置更新

因为 force 是引用的 d3 的 force,盲猜 Vue 可能和 d3 的 onTick 有点冲突?

Yanyan-Wang avatar Sep 02 '22 10:09 Yanyan-Wang

这个我试了一下 可以解决初始化时候的动画效果 初始化完毕拖拽节点发现还是没有碰撞动画

ShineLuLu avatar Sep 04 '22 08:09 ShineLuLu

这个我试了一下 可以解决初始化时候的动画效果 初始化完毕拖拽节点发现还是没有碰撞动画

你是不是没加 animate:true 我是有动画,但是碰撞检测有问题

ligouhai avatar Sep 05 '22 08:09 ligouhai

不用加 animate: true,在 layout 配置中加一行:onTick: () => graph.refreshPositions(), 相当于在布局过程中每一次迭代调用一次节点位置更新

因为 force 是引用的 d3 的 force,盲猜 Vue 可能和 d3 的 onTick 有点冲突?

你这样我试了下,确实能解决问题。但每次点击的话,会卡顿,还是没官网那样丝滑

ligouhai avatar Sep 05 '22 08:09 ligouhai

看了一下是最近的小版本有一些问题,刚刚发布了 4.7.1, 不需要配置 onTick,默认带有动画,你们试试。

Yanyan-Wang avatar Sep 05 '22 10:09 Yanyan-Wang

看了一下是最近的小版本有一些问题,刚刚发布了 4.7.1, 不需要配置 onTick,默认带有动画,你们试试。

好的,我再试试

ligouhai avatar Sep 08 '22 01:09 ligouhai

看了一下是最近的小版本有一些问题,刚刚发布了 4.7.1, 不需要配置 onTick,默认带有动画,你们试试。

依旧还是不行,目前引用的最新版本4.7.2,但拖动气泡时候,周边气泡碰撞时无动画

ligouhai avatar Sep 08 '22 02:09 ligouhai

我也有这个问题

liect avatar Sep 16 '22 03:09 liect

在上面 demo 来看好像没有问题,出现问题的 demo 再给一个?

Yanyan-Wang avatar Nov 29 '22 10:11 Yanyan-Wang

尊敬的用户,您好。我们很重视您的 issue,但由于我们没有得到可追踪复现的 demo,我们暂时认为这个问题已经解决。如果还有任何问题,请随时根据 issue 模版再开启新的 issue。

Yanyan-Wang avatar Dec 26 '22 07:12 Yanyan-Wang