Я учусь cytoscape.js
и связанного с ним расширения cytoscape.js-expand-collapse
и меня привлекают его удивительные возможности.
Я попытался сделать демонстрацию, котораякогда откроете узел, запросите дочерние узлы с помощью ajax с сервера и разверните группу.
Я считаю, что расширение cytoscape.js-expand-collapse
имеет утилиту cue.Когда у узла есть дочерние узлы, он будет показывать значок cue.
В моей демонстрации я не знаю дочерние узлы, прежде чем развернуть его.Поэтому я добавляю фиктивный узел в исходное состояние.
При развертывании узла мне нужно удалить фиктивный узел, запросить реальные дочерние узлы, добавить в группу и ретранслировать график.
Как выполнить ретрансляцию?Я нахожу два пути.Один из способов - вызвать expand
API расширения, другой - вызвать cy.layout()
.Ни один из методов не является удовлетворительным.
Если я разверну узел с помощью expand
метода cytoscape.js-expand-collapse
, весь график всегда будет смещаться на сцену или даже вылетать за пределы экрана.
param.options.layoutBy = {
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
};
api.expand(param.nodes, param.options);
Если развернуть узел с помощью cy.layout({name:'cose-bilkent'})
, компоновка выглядит хорошо, но значок метки неправильный.(При развертывании узла должен отображаться значок минус, а не значок плюс)
var layout = cy.layout({
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
});
layout.run();
Мне нужно объединить преимущества!При развертывании узла макет выглядит хорошо, а значок метки правильный.
Смотрите мою демонстрацию.Обновите демо-версию, чтобы исправить некоторые проблемы, теперь моя демоверсия может хорошо работать.
document.addEventListener('DOMContentLoaded', function(){
initGraph();
});
function initGraph(){
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function(){
var api = window.api = this.expandCollapse({
layoutBy: {
name: "cose-bilkent",
animate: "end",
randomize: false,
fit: false
},
fisheye: false,
animate: false,
undoable: true
});
api.collapseAll();
},
style: [
{
selector: 'node',
style: {
'background-color': '#ad1a66',
'label':'data(name)',
}
},
{
selector: ':parent',
style: {
'background-opacity': 0.333
}
},
{
selector: "node.cy-expand-collapse-collapsed-node",
style: {
"background-color": "darkblue",
"shape": "rectangle"
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ad1a66'
}
}
],
elements: initData()
});
initListeners();
cy.on('layoutstop', centerGraph);
function centerGraph(){
cy.center();
cy.off('layoutstop', centerGraph);
}
}
function initData(){
var me = this, i;
var groupNum = 5, groups = [], dummy = [], edges = [];
for(i = 0; i < groupNum; i++){
groups.push({
group:'nodes',
data:{
id:'group'+i,
name:'group'+i
}
});
dummy.push({// Insert dummy node in order to show cue icon.
group:'nodes',
data:{
id:'dummy'+i,
name:'dummy'+i,
parent : 'group'+i
}
});
}
var s,t;
for(i = 0; i < groupNum - 1; i++){
s = dummy[i].data.id;
for(j = i+1; j < groupNum; j++){
t = dummy[j].data.id;
edges.push({"group":'edges',data:{"id":s+"_"+t, source:s, target:t}});
}
}
return groups.concat(edges).concat(dummy);
}
function initListeners(){
var ur = cy.undoRedo();
ur.action("expand", function(param){
var collapsedChildren = param.nodes._private.data.collapsedChildren;
if(collapsedChildren
&& collapsedChildren.length == 1
&& collapsedChildren[0]._private.data.id.indexOf('dummy') >= 0){
openTargetNode(param.nodes);
param.options.layoutBy = {
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
};
if(document.getElementById('switch').checked){
var layout = cy.layout({
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
});
layout.run();
return;
}
}else{
param.options.layoutBy = null;
}
param.options.layoutBy = {
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
};
api.expand(param.nodes, param.options);
});
ur.action("collapse", function(param){
param.options.layoutBy = {
name: 'cose-bilkent',
fit: false,
randomize: false,
idealEdgeLength : 150
};
api.collapse(param.nodes, param.options);
});
}
function openTargetNode(target){
var me = this;
var targetPosition = target.position();
var childNum = 5, innerLinks = 10, i, j, counter = 0;
for(i = 0; i < childNum; i++){
cy.add({
group: 'nodes',
data: { id: target.id() + i, name: target.id() + i, parent: target.id() },
position:{x:targetPosition.x, y: targetPosition.y}
});
}
for(i = 0; i < childNum - 1; i++){
s = target.id() + i;
for(j = i + 1; j < childNum && counter++ < innerLinks; j++){
t = target.id() + j;
cy.add({
group: 'edges',
data: { id: s+"-"+t, source: s, target: t }
});
}
}
var neighborhood = target.neighborhood(), nodes = [];
for(i = 0; i <neighborhood.length; i++){
if(target.neighborhood()[i].isNode()){
nodes.push(target.neighborhood()[i]._private.data.id);
}
}
var s, t, j = 0;
for(i = 0; i < nodes.length; i++){
s = target.id() + (j++)%childNum;
t = nodes[i];
cy.add({
group: 'edges',
data: { id: s+"-"+t, source: s, target: t }
});
}
cy.remove(cy.$('#' + target._private.data.collapsedChildren[0]._private.data.id));
cy.remove(target.connectedEdges());
}
function switchHandler(a, b){
initGraph();
}
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
z-index: 999;
width: 85%;
height: 85%;
float: left;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape@3.1.0/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/cytoscape-cose-bilkent@4.0.0/cytoscape-cose-bilkent.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape.js-undo-redo@1.0.1/cytoscape-undo-redo.js"></script>
<script src="https://unpkg.com/cytoscape-expand-collapse@3.1.1/cytoscape-expand-collapse.js"></script>
<div>
<input id="switch" type="checkbox" onclick=switchHandler(this)>Layout without cytoscape-expand-collapse</input>
</div>
<div id="cy"></div>