はじめに
CYtoscapeJSでのlabelの付け方は以下の種類が用意されています。
そして、Styleにはこのようにプロパティ定義をすることで実現できます。
ではもし、例えば下記画像のように複合ノード内にタイトルを含めたい場合はどうすればいいのでしょうか?
以下、本記事ではノードを包括する親ノードを複合ノードと呼びます。
indicates the compound node parent id
Elements JSON
より。
結論
/** TextMetricsの取得 */
const getMeasureText = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): TextMetrics => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext( '2d' )!;
const fontStyle = target.style('font-style');
const fontSize = target.style('font-size');
const fontFamily = target.style('font-family');
const fontWeight = target.style('font-weight');
ctx.font = fontStyle + ' ' + fontWeight + ' ' + fontSize + ' ' + fontFamily;
return ctx.measureText(target.data('name'));
}
/** 横幅の取得 */
const getCharacterWidthInCanvas = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): number => {
const labelWidthMargin = 10;
const measure = getMeasureText(target);
return measure.width + labelWidthMargin;
}
/** 高さの取得 */
const getCharacterHeightInCanvas = (target: cytoscape.NodeCollection | cytoscape.EdgeCollection): number => {
const labelHeightMargin = 10;
const measure = getMeasureText(target);
return measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent + labelHeightMargin;
}
/** --- generate cytoscape --- */
cytoscape({
container: /** target dom */,
layout: /** layout */,
elements: {
nodes: /** nodes */,
edges: /** edges */,
},
style: [
{
selector: 'node',
css: {
content: 'data(name)',
width: getCharacterWidthInCanvas,
shape: 'round-rectangle',
'text-valign': 'center',
'text-halign': 'center',
},
},
{
selector: ':parent',
css: {
padding: getCharacterHeightInCanvas,
'text-valign': 'bottom',
'text-halign': 'center',
// @ts-ignore
'text-margin-y': (node: cytoscape.NodeCollection) => -getCharacterHeightInCanvas(node),
},
},
],
});
複合ノードに padding
と text-margin-y
を与えてやります。
説明
getCharacterWidthInCanvas
と getCharacterHeightInCanvas
で与えられた文字列の横幅と高さを計算しています。(【CytoscapeJS】The style value of label is deprecated for widthを参考にしてください)
また、見栄えも気にして+αで固定パディング値も与えてやりましょう。
すでに node
に対してcssで content: 'data(name)'
を与えているため、複合ノードも囲っているとはいえノードの一種、ラベルが適用されます。
CytoscapeJSのつまづきポイントとして、ノードを囲う、いわゆるグループの役割を果たすノードが親ノードとして parent
で定義します。木構造を知っていると親ノードはより上位のノードを指すため、parentでエッジを貼るのかと思えますが、実際は複合ノードをparentで定義します。私はここでつまづきました。
さて、CSSの擬似クラスチックに selector: ':parent'
と定義してやり複合ノードを指定します。
その中にパディングでラベルを入れられるだけのパディングを作成します。これは用いるレイアウトエンジンによりますが、ラベルとノードがかぶる危険を排除するためです。
そして空いたスペースに text-margin-y
でラベルを動かして挿入します。負の値を与えることでマージンがめり込むので、それを活用しています。
@types/cytoscapeを利用しているのですが、型定義が上手く聞いてくれなかったため、 @ts-ignore
をつけています。
これで複合ノード内にラベルを配置することができました!
おわりに
2022年8月現在、cytoscapeJSの型定義ファイルでは型定義が間違っているように思える部分がいくつかあります。そういうときはあまり好ましく無いですが、ts-ignoreや型アサーションなどを活用しましょう。