G6的插件系統

G6的插件系統作的至關完善, 惋惜文檔沒有具體說到. 這裏整理一下g6的插件.javascript

插件大體分爲四種類型:java

  1. behaviour 行爲, 能夠理解爲事件處理
  2. node, edge的插件, 就是node和edge的樣式, 一樣是插件
  3. layout插件, node的佈局之類, 這部分涉及的算法比較多
  4. Util插件, 就是自定義工具函數, 將其內置G6.Util中

這四種插件都有各自的寫法以及api, 可是文檔中沒有提到, 這裏簡單介紹一下. 一下都以官方插件爲例.node

behaviour 行爲

寫完發現其實官方有這部分的文檔: www.yuque.com/antv/g6/cus…算法

請看下面代碼, 這部分是註冊一個右鍵拖動的行爲:api

// g6/plugins/behaviour.analysis/index.js
function panCanvas(graph, button = 'left', panBlank = false) {
  let lastPoint;
  if (button === 'right') {
    graph.behaviourOn('contextmenu', ev => {
      ev.domEvent.preventDefault();
    });
  }
  graph.behaviourOn('mousedown', ev => {
    if (button === 'left' && ev.domEvent.button === 0 ||
    button === 'right' && ev.domEvent.button === 2) {
      if (panBlank) {
        if (!ev.shape) {
          lastPoint = {
            x: ev.domX,
            y: ev.domY
          };
        }
      } else {
        lastPoint = {
          x: ev.domX,
          y: ev.domY
        };
      }
    }
  });


// 鼠標右鍵拖拽畫布空白處平移畫布交互
G6.registerBehaviour('rightPanBlank', graph => {
  panCanvas(graph, 'right', true);
})
複製代碼

而後在實例化graph的時候在modes中引入:dom

new Graph({
  modes: {
    default: ['panCanvas']
  }
})
複製代碼

其實到這裏咱們已經知道了, 只要是在一些內置事件中註冊一下自定義事件再引入咱們就能夠稱之爲一個行爲插件. 可是咱們還須要再深刻一點, 看究竟是不是這樣的.函數

// g6/src/mixin/mode.js
behaviourOn(type, fn) {
    const eventCache = this._eventCache;
    if (!eventCache[type]) {
      eventCache[type] = [];
    }
    eventCache[type].push(fn);
    this.on(type, fn);
},
複製代碼

照老虎畫貓咱們最終能夠實現一個本身的行爲插件:工具

// 未通過驗證
function test(graph) {
 graph.behaviourOn('mousedown' () => alert(1) ) 
}


// 鼠標右鍵拖拽畫布空白處平移畫布交互
G6.registerBehaviour('test', graph => {
  test(graph);
})

new Graph({
  modes: {
    default: ['test']
  }
})
複製代碼

node, edge的插件

關於node, edge的插件的插件其實官方文檔上面的自定義形狀和自定義邊.佈局

// g6/plugins/edge.polyline/index.js
G6.registerEdge('polyline', {
  offset: 10,
  getPath(item) {
    const points = item.getPoints();
    const source = item.getSource();
    const target = item.getTarget();
    return this.getPathByPoints(points, source, target);
  },
  getPathByPoints(points, source, target) {
    const polylinePoints = getPolylinePoints(points[0], points[points.length - 1], source, target, this.offset);
    // FIXME default
    return Util.pointsToPolygon(polylinePoints);
  }
});

G6.registerEdge('polyline-round', {
  borderRadius: 9,
  getPathByPoints(points, source, target) {
    const polylinePoints = simplifyPolyline(
      getPolylinePoints(points[0], points[points.length - 1], source, target, this.offset)
    );
    // FIXME default
    return getPathWithBorderRadiusByPolyline(polylinePoints, this.borderRadius);
  }
}, 'polyline');

複製代碼

這部分那麼多代碼其實最重要的仍是上面的部分, 註冊一個自定義邊, 直接引入就能夠在shape中使用了, 具體就不展開了. 自定義邊 自定義節點ui

layout插件

layout在初始化的時候便可以在 layout 字段中初始化也能夠在plugins中.

const graph = new G6.Graph({
  container: 'mountNode',
  layout: dagre
})

/* ---- */

const graph = new G6.Graph({
  container: 'mountNode',
  plugins: [ dagre ]
})
複製代碼

緣由在於寫插件的時候同時也把佈局註冊爲一個插件了:

// g6/plugins/layout.dagre/index.js
class Plugin {
  constructor(options) {
    this.options = options;
  }
  init() {
    const graph = this.graph;
    graph.on('beforeinit', () => {
      const layout = new Layout(this.options);
      graph.set('layout', layout);
    });
  }
}

G6.Plugins['layout.dagre'] = Plugin;
複製代碼

經過查看源碼咱們能夠知道自定義佈局的核心方法就是execute, 再具體一點就是咱們須要在每一個佈局插件中都有execute方法:

// g6/plugins/layout.dagre/layout.js
// 執行佈局
  execute() {
    const nodes = this.nodes;
    const edges = this.edges;
    const nodeMap = {};
    const g = new dagre.graphlib.Graph();
    const useEdgeControlPoint = this.useEdgeControlPoint;
    g.setGraph({
      rankdir: this.getValue('rankdir'),
      align: this.getValue('align'),
      nodesep: this.getValue('nodesep'),
      edgesep: this.getValue('edgesep'),
      ranksep: this.getValue('ranksep'),
      marginx: this.getValue('marginx'),
      marginy: this.getValue('marginy'),
      acyclicer: this.getValue('acyclicer'),
      ranker: this.getValue('ranker')
    });
    g.setDefaultEdgeLabel(function() { return {}; });
    nodes.forEach(node => {
      g.setNode(node.id, { width: node.width, height: node.height });
      nodeMap[node.id] = node;
    });
    edges.forEach(edge => {
      g.setEdge(edge.source, edge.target);
    });
    dagre.layout(g);
    g.nodes().forEach(v => {
      const node = g.node(v);
      nodeMap[v].x = node.x;
      nodeMap[v].y = node.y;
    });
    g.edges().forEach((e, i) => {
      const edge = g.edge(e);
      if (useEdgeControlPoint) {
        edges[i].controlPoints = edge.points.slice(1, edge.points.length - 1);
      }
    });
  }
複製代碼

上面是官方插件有向圖的核心代碼, 用到了dagre算法, 再簡化一點其實能夠理解爲就是利用某種算法肯定節點和邊的位置.

最終執行佈局的地方:

// g6/src/controller/layout.js
graph._executeLayout(processor, nodes, edges, groups)
複製代碼

Util插件

這類插件相對簡單許多, 就是將函數內置到Util中. 最後直接在G6.Util中使用便可

好比一個生成模擬數據的:

// g6/plugins/util.randomData/index.js
const G6 = require('@antv/g6');
const Util = G6.Util;
const randomData = {
  // generate chain graph data
  createChainData(num) {
    const nodes = [];
    const edges = [];
    for (let index = 0; index < num; index++) {
      nodes.push({
        id: index
      });
    }
    nodes.forEach((node, index) => {
      const next = nodes[index + 1];
      if (next) {
        edges.push({
          source: node.id,
          target: next.id
        });
      }
    });
    return {
      nodes,
      edges
    };
  },
  // generate cyclic graph data
  createCyclicData(num) {
    const data = randomData.createChainData(num);
    const { nodes, edges } = data;
    const l = nodes.length;
    edges.push({
      source: data.nodes[l - 1].id,
      target: nodes[0].id
    });
    return data;
  },
  // generate num * num nodes without edges
  createNodesData(num) {
    const nodes = [];
    for (let index = 0; index < num * num; index++) {
      nodes.push({
        id: index
      });
    }
    return {
      nodes
    };
  }
};
Util.mix(Util, randomData);
複製代碼
相關文章
相關標籤/搜索