有以下的一份数据,如何使用 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();
到这里为止,咱们也就实现了让两个节点之间展现多条边的功能。