2018年11月7日水曜日

Cytoscape.js Tips 集

概要

Cytoscape.js の Tips を紹介します

環境

  • macOS 10.14
  • Chrome 70.0.3538.77
  • Cytoscape.js 3.2.19

四角形のノードを作成する

style: [
  {
    selector: 'node',
    style: {
      'shape': 'rectangle',
      'background-color': '#666',
      'label': 'data(id)'
    }
  }
]

style'shape': 'rectangle' を指定する

丸と四角のノードを混在させる方法

elements: {
  nodes: [
    {
      data: {
        id: 'a',
      },
      classes: 'foo'
    },
    {
      data: { id: 'b' }
    }
  ],
  edges: [
    {
      data: { id: 'ab', source: 'a', target: 'b' }
    }
  ]
}

nodes で classes 属性を追加します
そして styleselector でクラスを指定します

style: [
  {
    selector: 'node',
    style: {
      'background-color': '#666',
      'label': 'data(id)'
    }
  },
  {
    selector: '.foo',
    style: {
      'shape': 'rectangle',
      'background-color': '#BD4343',
      'label': 'data(id)'
    }
  },
  {
    selector: 'edge',
    style: {
      'width': 3,
      'line-color': '#ccc',
      'target-arrow-color': '#ccc',
      'target-arrow-shape': 'triangle'
    }
  }
]

selector: 'node' がデフォルトで selector: '.foo'classes 属性を追加したスタイルになります

マウスオーバー時のイベントをハンドリングする

cy.on('mouseover', 'node', function(event) {
  console.log(event.target._private.data.id);
});

ノードに背景画像を設定する

{
  selector: '.host',
  style: {
    'shape': 'rectangle',
    'label': 'data(id)',
    'background-fit': 'cover',
    'background-image': 'https://farm8.staticflickr.com/7272/7633179468_3e19e45a0c_b.jpg'
  }
}

ノードのボーダ線を表示する

{
  selector: '.host',
  style: {
    'shape': 'rectangle',
    'label': 'data(id)',
    'border-color': '#BD4343',
    'border-width': 3,
    'border-opacity': 0.5
  }
}

ノードをタップしたら接続しているノードを削除する

cy.on('tap', 'node', function() {
  console.log('hoge');
  if (this.scratch().restData == null) {
    this.scratch({
      restData: this.successors().targets().remove()
    });
  } else {
    this.scratch().restData.restore();
    this.scratch({
      restData: null
    });
  }
});

source 側をタップすると target 側のノードが消えます

アニメーションを追加する

cy.nodes('.host').animate({
  position: {
    x: cy.nodes('.host').position('x') + 100,
    y: cy.nodes('.host').position('y')
  },
  style: {
    backgroundColor: 'red',
    'opacity': 0.0
  },
  duration: 1000,
  complete: function() {
    console.log("done");
    // cy.nodes('.host').remove();
  }
});

画面描画時に nodes('.host') が水平方向に右側に +100 動きます
またアニメーション時にノードの背景を赤に透明度を 0 にしています
つまりアニメーションが終了するとノードが見えなくなります (実際にはある)
また complete を指定するとアニメーション終了後に関数を呼び出すことができます
ノードを削除したい場合はここで remove() すれば OK です

応用: タップしたらアニメーションしてノードの削除を行う

cy.on('tap', '.router', function() {
  var tappedNode = this;
  var targetNode = this.successors().targets();
  if (tappedNode.scratch().restData == null) {
    var orgX = targetNode.position('x');
    targetNode.animate({
      position: {
        x: tappedNode.position('x') + 100,
        y: tappedNode.position('y')
      },
      style: {
        'opacity': 0.1
      },
      complete: function() {
        tappedNode.scratch({
          targetNode: targetNode,
          orgX: orgX,
          restData: targetNode.remove()
        });
      }
    });
  } else {
    tappedNode.scratch().restData.restore();
    tappedNode.scratch().targetNode.animate({
      position: {
        x: tappedNode.scratch().orgX,
        y: tappedNode.scratch().targetNode.position('y')
      },
      style: {
        'opacity': 1.0
      },
      complete: function() {
        console.log("restore");
      }
    });
    tappedNode.scratch({
      restData: null
    })
  }
});

restoreremove() した状態に戻すだけなのでアニメーションした後の位置でノードは復活します
なので restore したあとでノードを元の位置に戻してあげる必要があります
orgX は元の位置を保存するための scratch でこれを使って元の位置に戻します
少し長いですがやっていることは単純です

データを外出しする

elements と style の JSON データは外出しておくと便利です

Promise.all([
  fetch('../json/data.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    }),
  fetch('../json/style.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    })
])
.then(function(dataArray) {
  var cy = cytoscape({
    container: $('#cy'),
    elements: dataArray[0],
    style: dataArray[1],
    layout: {
      name: 'cose',
    }
  });
});

ただし json ファイルはローカルファイルは参照できないのでご注意を
Web アプリケーション化すれば問題ないです
Promiss.all を使うとキレイに書けます

応用: マウスオーバー時に子ノードをすべてハイライトする

またそれ以外のノードはローライトします

cy.on('mouseover', 'node', function(evt){
  var sel = evt.target;
  cy.elements().difference(sel.successors()).not(sel).addClass('semitransp');
  sel.addClass('highlight').successors().addClass('highlight');
});
cy.on('mouseout', 'node', function(evt){
  var sel = evt.target;
  cy.elements().removeClass('semitransp');
  sel.removeClass('highlight').successors().removeClass('highlight');
});

スタイルの json の定義は以下の通りです
色や透明度は好きな値に変更してください

{
  "selector": "node.highlight",
  "style": {
    "border-color": "#000",
    "border-width": "2px"
  }
},
{
  "selector": "node.semitransp",
  "style":{ 
    "opacity": "0.2"
  }
},
{
  "selector": "edge.highlight",
  "style": {
    "mid-target-arrow-color": "#000"
  }
},
{
  "selector": "edge.semitransp",
  "style":{
    "opacity": "0.2"
  }
}

ちなみに successorsoutgoers に変更すると 1 階層だけハイライトすることができます

filter を使って特定の data を含むノードだけを取得する

例えば name 属性を部分一致で検索して特定の文字列が含まれる場合のノードを取得します

var nodes = cy.nodes().filter(function(ele) {
  if (ele.data('name').indexOf(name) > -1) {
    return ele
  }
});

特定のノード以外のノードたちを取得する

cy.nodes().difference(nodes).addClass('semitransp');

上記の場合 nodes 以外に対してクラスを追加します

0 件のコメント:

コメントを投稿