有以下的一份數據,如何使用 G6 讓兩個節點之間連多條邊?javascript
const data = { nodes: [{ id: 'node1', x: 100, y: 150, label: 'node1', }, { id: 'node2', x: 300, y: 150, label: 'node2' }], edges: [{ source: 'node1', target: 'node2' }, { source: 'node2', target: 'node1' } ] };
接到上面這個問題後,咱們立刻就開始動手,二話不說先擼出了下面這段代碼。java
const graph = new G6.Graph({ container: GRAPH_CONTAINER, width: 500, height: 500, defaultNode: { style: { fill: '#DEE9FF', stroke: '#5B8FF9' }, labelCfg: { style: { fontSize: 12 } } }, defaultEdge: { style: { stroke: '#e2e2e2' } } }); graph.data(data); graph.render();
看起來心情還不錯,給節點和邊都設置了樣式。node
運行上面的代碼之後,卻發現現實是如此的殘酷,說好的兩條邊哪去了,怎麼還只有一條呢?git
難道是線太直了,果斷換成貝塞爾曲線。github
const graph = new G6.Graph({ // ... defaultEdge: { shape: 'cubic', style: { stroke: '#e2e2e2' } } }); // ...
難道是彎過頭,怎麼仍是這個鬼樣子?ide
對了,G6 還支持 quadratic
類型的邊,也拿來試試吧。this
const graph = new G6.Graph({ // ... defaultEdge: { shape: 'quadratic', style: { stroke: '#e2e2e2' } } }); // ...
神奇,竟然能夠了。code
這個時候,忽然想起,G6 3.1 版本新增了一個 polyline
類型的邊。blog
const graph = new G6.Graph({ // ... defaultEdge: { shape: 'polyline', style: { stroke: '#e2e2e2' } } }); // ...
拖動下節點看看。ip
這什麼鬼東西,看來仍是 quadratic
香啊。
這個時候,PD 忽然說到,兩個節點之間須要顯示3條邊,what,這不 so easy 嗎?
年輕的你很開心地把數據改爲了這樣:
const data = { nodes: [{ id: 'node1', x: 100, y: 150, label: 'node1', }, { id: 'node2', x: 300, y: 150, label: 'node2' }], edges: [{ source: 'node1', target: 'node2' }, { source: 'node2', target: 'node1' }, { source: 'node2', target: 'node1' } ] };
然而,執行結果卻很骨感。
新加的邊哪去了?
就正在你 百思不得騎姐其解 的時候,忽然靈光一閃,想到了原來 G6 仍是強大的黑科技「自定義邊」。
有了這個黑科技,什麼樣的需求,那還不是分分鐘的事。
固然了,在使用「自定義邊」的以前,有兩件事仍是須要明確下的:
咱們就在邊數據中添加一個 edgeType 用於區分不一樣的邊。有了這個約定之後,就能夠開始動手擼碼了。
自定義邊後的效果以下:
完善的自定義邊的代碼以下:
const edgeTypeColorMap = { type1: ['#531dab', '#391085', '#391085'], type2: ['#d9d9d9', '#bfbfbf', '#8c8c8c'], type3: ['#d3adf7', '#b37feb', '#9254de'] } const defaultConf = { style: { lineAppendWidth: 5, lineDash: [0, 0], lineDashOffset: 0, opacity: 1, labelCfg: { style: { fillOpacity: 1 } } }, /** * 繪製邊 * @override * @param {Object} cfg 邊的配置項 * @param {G.Group} group 邊的容器 * @return {G.Shape} 圖形 */ drawShape(cfg, group) { const item = group.get('item') const shapeStyle = this.getShapeStyle(cfg, item); const shape = group.addShape('path', { className: 'edge-path', attrs: shapeStyle }); return shape; }, drawLabel(cfg, group) { const labelCfg = cfg.labelCfg || {} const labelStyle = this.getLabelStyle(cfg, labelCfg, group) const text = group.addShape('text', { attrs: { ...labelStyle, text: cfg.label, fontSize: 12, fill: '#404040', cursor: 'pointer' }, className: 'edge-label' }) return text }, /** * 獲取圖形的配置項 * @internal 僅在定義這一類節點使用,用戶建立和更新節點 * @param {Object} cfg 節點的配置項 * @return {Object} 圖形的配置項 */ getShapeStyle(cfg, item) { const { startPoint, endPoint } = cfg const type = item.get('type') const defaultStyle = this.getStateStyle('default', true, item) if(type === 'node') { return Object.assign({}, cfg.style, defaultStyle); } const controlPoints = this.getControlPoints(cfg); let points = [ startPoint ]; // 添加起始點 // 添加控制點 if (controlPoints) { points = points.concat(controlPoints); } // 添加結束點 points.push(endPoint); const path = this.getPath(points); const style = Object.assign({}, { path }, cfg.style, defaultStyle); return style; }, getControlPoints(cfg) { let controlPoints = cfg.controlPoints; // 指定controlPoints if (!controlPoints || !controlPoints.length) { const { startPoint, endPoint } = cfg; const innerPoint = G6.Util.getControlPoint(startPoint, endPoint, 0.5, cfg.edgeOffset || 30); controlPoints = [ innerPoint ]; } return controlPoints; }, /** * 獲取三次貝塞爾曲線的path * * @param {array} points 起始點和兩個控制點 * @returns */ getPath(points) { const path = []; path.push([ 'M', points[0].x, points[0].y ]); path.push([ 'Q', points[1].x, points[1].y, points[2].x, points[2].y ]); return path; }, /** * 根據不一樣狀態,獲取不一樣狀態下的樣式值 * @param {string} name * @param {string} value * @param {Item} item */ getStateStyle(name, value, item) { const model = item.getModel() const { style = {} } = model const defaultStyle = Object.assign({}, this.style) // 更新顏色 return { ...defaultStyle, lineWidth: 1, stroke: edgeTypeColorMap[model.edgeType] && edgeTypeColorMap[model.edgeType][0], ...style } }, /** * 拖動時更新path及邊的label * * @param {object} cfg 邊的model * @param {Edge} item 邊的實例 */ update(cfg, item) { const { data, style, startPoint, endPoint, labelCfg = {} } = cfg const group = item.getContainer() const model = data || cfg const defaultStyle = Object.assign({}, this.style, { lineWidth: 1, stroke: edgeTypeColorMap[model.edgeType] && edgeTypeColorMap[model.edgeType][0] }, style) const { opacity, onlyHideText } = defaultStyle // 更新 path const keyShape = item.getKeyShape(); const controlPoints = this.getControlPoints(cfg); keyShape.attr({ path: [ ['M', startPoint.x, startPoint.y], ['Q', controlPoints[0].x, controlPoints[0].y, endPoint.x, endPoint.y] ], ...defaultStyle }); const labelStyle = this.getLabelStyle(cfg, labelCfg, group); const text = group.findByClassName('edge-label'); const attrs = { ...labelStyle, fillOpacity: onlyHideText ? 0 : opacity === 0 ? opacity : 1, fill: '#404040' } if(text) { text.resetMatrix(); text.attr(attrs); } } }; G6.registerEdge('quadratic-label-edge', defaultConf, 'quadratic'); const GRAPH_CONTAINER = 'container'; const data = { nodes: [{ id: 'node1', x: 100, y: 150, label: 'node1', }, { id: 'node2', x: 300, y: 150, label: 'node2' }], edges: [{ source: 'node1', target: 'node2', edgeType: 'type1' }, { source: 'node2', target: 'node1', edgeType: 'type2' }, { source: 'node2', target: 'node1', edgeType: 'type3', edgeOffset: -20 } ] }; const graph = new G6.Graph({ container: GRAPH_CONTAINER, width: 500, height: 500, modes: { default: [{ type: 'drag-node', delegate: false }] }, defaultNode: { style: { fill: '#DEE9FF', stroke: '#5B8FF9' }, labelCfg: { style: { fontSize: 12 } } }, defaultEdge: { shape: 'quadratic-label-edge', } }); graph.data(data); graph.render();
到這裏爲止,咱們也就實現了讓兩個節點之間展現多條邊的功能。