G6的插件系統作的至關完善, 惋惜文檔沒有具體說到. 這裏整理一下g6的插件.javascript
插件大體分爲四種類型:java
這四種插件都有各自的寫法以及api, 可是文檔中沒有提到, 這裏簡單介紹一下. 一下都以官方插件爲例.node
寫完發現其實官方有這部分的文檔: 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的插件的插件其實官方文檔上面的自定義形狀和自定義邊.佈局
// 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
字段中初始化也能夠在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中. 最後直接在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);
複製代碼