フローチャート作成・描画をマウス操作で行い、データ連携させる
フローチャートをGUIで作図し、一覧のデータと連携させるということをやったので備忘録です。
主にライブラリに頼りますので、以下のようなものを使いました。
- vis.js
- joint.js
vis.js
vis.jsのサンプルです。
オプションについては公式に詳細があるのでそちらを。
vis.js
data: any;
network: Network;
nodes: any;
edges: any;
const nodeData: any[] = [];
for (const element of ELEMENTLIST) {
nodeData.push({id: element.elementId, label: element.elementName});
}
const nodes = new DataSet(nodeData);
const edges = new DataSet([
{from: '01', to: '02'},
{from: '01', to: '03'},
{from: '02', to: '04'},
{from: '02', to: '05'},
]);
// create a network
this.data = {
nodes: nodes,
edges: edges
};
const options = {
layout: {
hierarchical: {
sortMethod: 'directed'
},
},
interaction: {
dragNodes: false,
dragView: false,
hover: true,
},
manipulation: {
enabled: false,
initiallyActive: false,
addEdge: function (data, callback) {
console.log('add edge', data);
if (data.from === data.to) {
const r = confirm('Do you want to connect the node to itself?');
if (r === true) {
callback(data);
}
} else {
callback(data);
}
// after each adding you will be back to addEdge mode
this.network.addEdgeMode();
},
controlNodeStyle: {
// all node options are valid.
}
},
edges: {
arrows: 'to',
smooth: false
},
nodes: {
shape: 'box',
},
physics: {
enabled: true,
hierarchicalRepulsion: {
nodeDistance: 180,
},
solver: 'hierarchicalRepulsion'
}
};
this.network = new Network(this.visElement.nativeElement, this.data, options);
this.network.addEdgeMode();
joint.js
途中でこっちに乗り換えました。
使い勝手とカスタマイズ性がこちらの方が良さそうだったので。
this.graph = new joint.dia.Graph;
const paper = new joint.dia.Paper({
el: document.getElementById('myholder'),
width: '200vh',
height: '100vh',
model: this.graph,
background: {
color: '#eaeaea',
},
gridSize: 10,
drawGrid: {
color: '#efefef',
name: 'mesh'
},
restrictTranslate: true,
defaultRouter: { name: 'manhattan' },
defaultLink: new joint.dia.Link({
attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', stroke: '#505050', fill: '#505050' }, '.connection' : { stroke: '#505050', strokeWidth: 2 } }
}),
linkPinning: false,
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent linking from input ports.
if (magnetS && magnetS.getAttribute('port-group') === 'in') {
return false;
}
// Prevent linking from output ports to input ports within one element.
if (cellViewS === cellViewT) {
return false;
}
// Prevent linking to input ports.
return magnetT && magnetT.getAttribute('port-group') === 'in';
},
validateMagnet: function(cellView, magnet) {
// Note that this is the default behaviour. Just showing it here for reference.
// Disable linking interaction for magnets marked as passive (see below `.inPorts circle`).
return magnet.getAttribute('magnet') !== 'passive';
}
});
paper.scale(0.8);
エレメントの設定はこんな感じ。 詳細はhttp://visjs.org/docs/network/
export const layout = {
size: { width: 200, height: 80 },
inPorts: ['in'],
outPorts: ['out'],
ports: {
groups: {
'in': {
attrs: {
'.port-body': {
fill: '#16A085',
magnet: 'passive'
}
},
position: 'top',
},
'out': {
attrs: {
'.port-body': {
fill: '#E74C3C'
}
},
position: 'bottom'
}
}
},
attrs: {
'.label': { text: 'agra', 'ref-x': .5, 'ref-y': .2 },
rect: { fill: '#2ECC71' }
},
strokeWidth: 0,
cursor: 'pointer',
magnet: true,
label: {
fill: '#333333',
fontWeight: 'bold',
cursor: 'pointer',
},
rect: {
fill: 'red'
},
refPoints: '0,10 10,0 20,10 10,20',
filter: {
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3,
angle: 50,
}
},
defaultFill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#ffffff' },
{ offset: '100%', color: '#e2f1fc' },
],
attrs: { x1: '0%', y1: '0%', x2: '0%', y2: '100%' }
},
executingFill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#fac695' },
{ offset: '37%', color: '#f5ab66' },
{ offset: '100%', color: '#ef8d31' },
],
attrs: { x1: '0%', y1: '0%', x2: '0%', y2: '100%' }
},
};
リンク追加はこんな感じです。
new joint.dia.Link({
source: {
id: source,
port: ' '
},
target: {
id: target,
port: ''
},
attrs: {
'.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', stroke: '#505050', fill: '#505050' }, '.connection' : { stroke: '#505050', strokeWidth: 2 }
}
});
データ連携
作りたいエレメントとリンクの数だけ回せばデータができる。
エレメントの追加や動的なリンク情報取得は同様にaddしたり、portのリンク情報取得するだけ。
グラフのイベント発火を待ってデータ取得する。
this.paper.on('link:connect', (linkView, evt, cellView, magnet, arrowhead) => {
this.graph.getConnectedLinks(
this.graph.attributes.cells._byId[linkView.model.attributes.source.id],
{ outbound: true}
)
}
総括
ライブラリの使い方という感じになってしまいましたが、どのライブラリも情報少ないし使いづらい。