G6 icon indicating copy to clipboard operation
G6 copied to clipboard

⭐️📖 G6 使用案例收集 ( Example Contribution Wanted ) 📖⭐️

Open mxz96102 opened this issue 3 years ago • 1 comments

缘由 Cause

G6 现在已经正式迈入了4.0+时代,我们很欣慰的看到有越来越多的人使用G6,承蒙各位厚爱,G6目前也在越来越多的场景被使用。与此同时,G6原来的图表案例就相形见绌了,还停留在一个比较基本的案例阶段,他只是展示了一些基本的原子功能案例。所以在这里,我们向社区的各位发出请求,为了让对G6的使用方案有更好的沉淀,希望大家提供自己实际的使用案例,我们将会修改为G6官网的案例,并且附上你的署名,让你的案例在G6官网可以帮助更多的人。

G6 has now officially entered the 4.0+ era and we are pleased to see that more and more people are using G6, and thanks to your love, G6 is now being used in more and more scenarios. At the same time, G6's original diagramming examples are dwarfed by the fact that it is still at a rather basic case stage, he only shows some basic examples of atomic functionality. So here we send out a request to everyone in the community to provide their own actual use cases in order to allow a better sinking of the use scenarios for G6, which we will modify for the G6 website and include your byline so that your cases can help more people on the G6 website.

怎样贡献 How To Contribute

我们强烈推荐使用codesandbox这样的在线代码平台来贡献你的案例,这样可以让我们更直观的了解你的案例,并且能更好的转化为官网的案例。

We highly recommend using an online code platform such as codesandbox to contribute your case, as this will give us a more visual understanding of your case and will translate better into the official website.

首先我们推荐使用G6的code sandbox的案例,你需要fork这个案例,在这个案例上进行开发。

Firstly we recommend using the G6 code sandbox case, you will need to fork this case and develop on it.

Edit sharp-moore-xci4e

写好案例保存后,从github新建issue,选择Example Contribution模版,按步骤完成填写。

Once you have written your case and saved it, create a new issue from Github, select the Example Contribution template and follow the steps to complete it.

mxz96102 avatar Mar 01 '21 07:03 mxz96102

基于 G6 的图分析产品:GraphInsight:https://graphinsight.antgroup.com/home.html#/ A graph analysis product based on G6: https://graphinsight.antgroup.com/home.html#/

GraphInsight image

pomelo-nwu avatar Jun 06 '22 13:06 pomelo-nwu

Hello, I am a backend software engineer, and I just using G6 for temporary reasons.

Could you offer more complicated example like the pic in your doc? It`s hard for new comer to code for a little bit more complicated project, and to understand why combo force layout not work well for nodes in the combo?

image

logzhouchen avatar Nov 03 '22 13:11 logzhouchen

基于 G6 的大数据血缘分析产品:OpenLineage:https://openbytecode.com/openLineage/front/ A graph analysis product based on G6: https://openbytecode.com/openLineage/front/ github:https://github.com/lijunping365/Open-Lineage-Web

v1 0 0-2

lijunping365 avatar May 03 '23 08:05 lijunping365

基于G6的Web3可视化交易追溯和被盗分析产品:「ByteHunter」,支持ERC20、ERC721以及五条主流EVM公链 官网:https://bytehunter.site Dashboard(交易追溯+被盗分析):https://console.bytehunter.site image

Martin2037 avatar May 09 '23 06:05 Martin2037

学习了一天的才会的简单布局构建架构图

image

import { Graph as BaseGraph, extend, Extensions } from '@antv/g6';

const Graph = extend(BaseGraph, {
  edges: {
    'polyline-edge': Extensions.PolylineEdge,
    'custom-edge': Extensions.CubicEdge,
  },
});

const data = {
  nodes: [
    {
      id: '0',
      data: {
        label: 'A0',
        x: 110,
        y: 110,
      },
    },
    // B
    {
      id: '1',
      data: {
        label: 'B1',
        parentId: 'B',
        x: 110,
        y: 350,
      },
    },
    {
      id: '2',
      data: {
        label: 'B2',
        parentId: 'B',
        x: 220,
        y: 350,
      },
    },
    {
      id: '3',
      data: {
        label: 'B3',
        parentId: 'A',
        x: 360,
        y: 350,
      },
    },
    // C
    {
      id: '4',
      data: {
        label: 'C1',
        parentId: 'C',
        x: 360,
        y: 150,
      },
    },
    {
      id: '5',
      data: {
        label: 'C2',
        parentId: 'C',
        x: 460,
        y: 150,
      },
    },
  ],
  edges: [
    {
      id: 'edge-102',
      source: '0',
      target: '1',
      data: {
        type: 'custom-edge',
        animates: {
          update: [
            {
              // 在 selected 和 active 状态变化导致的 haloShape opacity 变化时,使 opacity 带动画地更新
              fields: ['opacity'],
              shapeId: 'haloShape',
              states: ['selected', 'active'],
              duration: 500,
            },
          ],
        },
      },
    },
    {
      id: 'edge-161',
      source: '0',
      target: '2',
      data: {},
    },
    {
      id: 'edge-253',
      source: '0',
      target: 'A',
      data: {
        keyShape: {
          endArrow: true,
        },
        labelShape: {
          text: 'asdf-arrow',
        },
      },
    },
    {
      id: 'edge-237',
      source: '1',
      target: '4',
      data: {
        type: 'polyline-edge',
        keyShape: {
          endArrow: true,
          routeCfg: {
            /**
             * 目前支持正交路由 'orth' 和地铁路由 'er'
             */
            // name: 'er',
            /**
             * 是否开启自动避障,默认为 false
             * Whether to enable automatic obstacle avoidance, default is false
             */
            enableObstacleAvoidance: true,
          },
          /**
           * 拐弯处的圆角弧度,默认为直角,值为 0
           * The radius of the corner rounding, defaults to a right angle
           */
          // radius: 20,
          /**
           * 拐弯处距离节点最小距离, 默认为 2
           * Minimum distance from the node at the corner, default is 5.
           */
          // offset: 0,
          /**
           * 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
           * An array of control points that, when not specified, automatically generates the bends according to the A* algorithm. If specified, bends are made at the position specified by controlPoints.
           */
          // controlPoints: [],
        },
      },
    },
    {
      id: 'edge-133',
      source: '2',
      target: '4',
      data: {
        type: 'polyline-edge',
        keyShape: {
          /**
           * 拐弯处的圆角弧度,默认为直角
           */
          radius: 20,
          /**
           * 拐弯处距离节点最小距离, 默认为 5
           */
          // offset: 0,
          /**
           * 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
           */
          controlPoints: [
            { x: 220, y: 220 },
            { x: 300, y: 130 },
          ],
        },
      },
    },
    {
      id: 'edge-320',
      source: '2',
      target: '5',
      data: {
        keyShape: {
          endArrow: true,
        },
        labelShape: {
          text: 'edge-arrow',
        },
      },
    },
    {
      id: 'edge-355',
      source: '3',
      target: '4',
      data: {
        keyShape: {
          endArrow: true,
        },
        labelShape: {
          text: 'asdf-arrow',
        },
      },
    },
  ],
  combos: [
    {
      id: 'A',
      data: {
        text: 'combo A',
      },
    },
    {
      id: 'B',
      data: {
        label: 'combo B',
      },
    },
    {
      id: 'C',
      data: {
        label: 'combo C',
      },
    },
  ],
};
const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
let graph = new Graph({
  container: 'container',
  width,
  height,
  plugins: [
    {
      // lod-controller will be automatically assigned to graph with `disableLod: false` to graph if it is not configured as following
      type: 'lod-controller',
      disableLod: true,
    },
  ],
  modes: {
    default: [
      {
        type: 'drag-node',
        enableTransient: false,
        updateComboStructure: false,
      },
      {
        type: 'click-select',
        itemTypes: ['node', 'edge', 'combo'],
      },
      {
        type: 'drag-combo',
        enableTransient: true,
        updateComboStructure: true,
      },
      // 'drag-combo',
      'drag-canvas',
      // 'click-select',
      'zoom-canvas',
    ],
  },

  theme: {
    type: 'spec',
    base: 'light',
    specification: {
      node: {
        dataTypeField: 'parentId',
      },
    },
  },
  node: (model) => {
    const { id, data } = model;
    return {
      id,
      data: {
        ...data,
        type: 'rect-node',
        keyShape: {
          width: 60,
          height: 30,
          radius: 8,
        },
        labelShape: {
          position: 'center',
          text: data.label,
        },
        anchorPoints: [
          [0.5, 0],
          [0.5, 1],
        ],
        animates: {
          update: [
            {
              fields: ['opacity'],
              shapeId: 'haloShape',
              states: ['breathing'],
              iterations: Infinity,
              direction: 'alternate',
              duration: 500,
            },
            {
              fields: ['lineWidth'],
              shapeId: 'keyShape',
              states: ['breathing'],
              iterations: Infinity,
              direction: 'alternate',
              duration: 500,
            },
            {
              fields: ['r'],
              shapeId: 'keyShape',
              states: ['scaling'],
              iterations: Infinity,
              direction: 'alternate',
              duration: 500,
            },
          ],
        },
      },
    };
  },
  edge: {
    type: 'custom-edge',
    animates: {
      update: [
        {
          fields: ['lineDash'],
          shapeId: 'keyShape',
          states: ['growing', 'running'],
          iterations: Infinity,
          duration: 2000,
        },
        {
          fields: ['offsetDistance'],
          shapeId: 'buShape',
          states: ['circleRunning'],
          iterations: Infinity,
          duration: 2000,
        },
      ],
    },
  },
  combo: (model) => {
    const { id, data } = model;
    return {
      id,
      data: {
        ...data,
        type: 'rect-combo',
        keyShape: {
          opacity: 0.8,
          padding: [20, 20, 20, 20],
          radius: 8,
        },
        labelShape: {
          text: data.label,
          offsetY: 8,
        },
        labelBackgroundShape: {},
        animates: {
          update: [
            {
              fields: ['width', 'height', 'x', 'y'],
              shapeId: 'keyShape',
            },
          ],
        },
      },
    };
  },
  nodeState: {
    breathing: {
      haloShape: {
        opacity: 0.25,
        lineWidth: 20,
        visible: true,
      },
      keyShape: {
        stroke: '#000',
        lineWidth: 4,
      },
    },
    scaling: {
      keyShape: {
        r: 24,
        width: 48,
        height: 48,
      },
    },
  },
  edgeState: {
    growing: {
      keyShape: {
        lineWidth: 2,
        lineDash: ['100%', 0],
      },
    },
    running: {
      keyShape: {
        lineWidth: 2,
        lineDash: [2, 2],
        //  TODO: lineDashOffset
      },
    },
  },
  data,
});

const actions = {
  'Enable/Disable Node States': {
    Breathing: () => {
      graph.getAllNodesData().forEach((node) => {
        if (graph.getItemState(node.id, 'breathing')) {
          graph.setItemState(node.id, 'breathing', false);
        } else {
          graph.setItemState(node.id, 'scaling', false);
          graph.setItemState(node.id, 'breathing', true);
        }
      });
    },
    Scaling: () => {
      graph.getAllNodesData().forEach((node) => {
        if (graph.getItemState(node.id, 'scaling')) {
          graph.setItemState(node.id, 'scaling', false);
        } else {
          graph.setItemState(node.id, 'breathing', false);
          graph.setItemState(node.id, 'scaling', true);
        }
      });
    },
  },
  'Enable/Disable Edge States': {
    Growing: () => {
      graph.getAllEdgesData().forEach((edge) => {
        if (graph.getItemState(edge.id, 'growing')) {
          graph.setItemState(edge.id, 'growing', false);
        } else {
          graph.setItemState(edge.id, 'running', false);
          graph.setItemState(edge.id, 'growing', true);
        }
      });
    },
    Running: () => {
      graph.getAllEdgesData().forEach((edge) => {
        if (graph.getItemState(edge.id, 'running')) {
          graph.setItemState(edge.id, 'running', false);
        } else {
          graph.setItemState(edge.id, 'growing', false);
          graph.setItemState(edge.id, 'running', true);
        }
      });
    },
  },
};

const controllerContainer = document.createElement('div');
controllerContainer.style.position = 'absolute';
container.appendChild(controllerContainer);

Object.keys(actions).forEach((groupName, i) => {
  const btnContainer = document.createElement('div');
  controllerContainer.appendChild(btnContainer);
  const tip = document.createElement('span');
  tip.innerHTML = `👉 ${groupName}: `;
  btnContainer.appendChild(tip);
  Object.keys(actions[groupName]).forEach((name, i) => {
    const btn = document.createElement('a');
    btn.innerHTML = name;
    btn.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
    btn.style.padding = '4px';
    btn.style.marginLeft = i > 0 ? '24px' : '8px';
    btnContainer.appendChild(btn);
    btn.addEventListener('click', () => {
      actions[groupName][name]();
    });
  });
});

window.graph = graph;

TsMask avatar Dec 07 '23 12:12 TsMask