使用 sigma.js 展开和折叠
Expand and collapse using sigma.js
目标
我正在尝试在 sigma.js 中实现展开和折叠。右键单击任何节点,它会添加新节点并连接其边缘,但它会放置在随机位置。
我想在空闲 space 中添加节点,它们不应与其他节点碰撞或重叠。它应该随着动画慢慢展开,在空闲 space 区域展开,如 this example. Related.
代码
<!DOCTYPE html>
<html>
<head>
<title> Airlines Graph Render </title>
<script src="../build/sigma.min.js"></script>
<script src="../src/renderers/canvas/sigma.canvas.edges.curvedArrow.js"></script>
<script src="../plugins/sigma.layout.forceAtlas2/worker.js"></script>
<script src="../plugins/sigma.layout.forceAtlas2/supervisor.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/settings.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curve.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.def.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curvedArrow.js"></script>
<style>
body,html{
width: 100%;
margin: 0px;
padding: 0px;
height: 100%
}
#graph-container {
width:100%;
height: 100%;
}
</style>
</head>
<body>
<div id="graph-container"></div>
<script >
var graph = {
"nodes": [
{
"city": "Dallas",
"area": 999,
"code": 214,
"country": "USA"
},
{
"city": "Austin",
"area": 1180,
"code": 512,
"country": "USA"
},
{
"city": "New York",
"area": 1214,
"code": 646,
"country": "USA"
},
{
"city": "Washington",
"area": 176,
"code": 564,
"country": "USA"
},
{
"city": "Atlanta",
"area": 342,
"code": 518,
"country": "USA"
},
{
"city": "Huston",
"area": 1625,
"code": 281,
"country": "USA"
},
{
"city": "Chicago",
"area": 606,
"code": 312,
"country": "USA"
},
{
"city": "London",
"area": 909,
"code": 312,
"country": "England"
}
],
"edges": [
{
"key": 1,
"source": "Dallas",
"destination": "Austin",
"distance": 200,
"airlines": "British Airways",
"fare": 220
},
{
"key": 2,
"source": "Austin",
"destination": "Dallas",
"distance": 200,
"airlines": "Lufthansa",
"fare": 120
},
{
"key": 3,
"source": "Washington",
"destination": "Dallas",
"distance": 1300,
"airlines": "Lufthansa",
"fare": 300
},
{
"key": 4,
"source": "Atlanta",
"destination": "Washington",
"distance": 600,
"airlines": "Lufthansa",
"fare": 600
},
{
"key": 5,
"source": "Washington",
"destination": "Atlanta",
"distance": 600,
"airlines": "KLM",
"fare": 400
},
{
"key": 6,
"source": "New York",
"destination": "Atlanta",
"distance": 300,
"airlines": "Qatar",
"fare": 1300
},
{
"key": 7,
"source": "Huston",
"destination": "Atlanta",
"distance": 800,
"airlines": "Indigo",
"fare": 400
},
{
"key": 8,
"source": "Atlanta",
"destination": "Huston",
"distance": 800,
"airlines": "Spicejet",
"fare": 600
},
{
"key": 9,
"source": "New York",
"destination": "Chicago",
"distance": 1000,
"airlines": "Air China",
"fare": 500
},
{
"key": 10,
"source": "Chicago",
"destination": "New York",
"distance": 1000,
"airlines": "Jet Airways",
"fare": 200
},
{
"key": 11,
"source": "Dallas",
"destination": "Chicago",
"distance": 900,
"airlines": "Lufthansa",
"fare": 1300
},
{
"key": 12,
"source": "Austin",
"destination": "Huston",
"distance": 160,
"airlines": "Lufthansa",
"fare": 240
},
{
"key": 13,
"source": "Dallas",
"destination": "New York",
"distance": 780,
"airlines": "Lufthansa",
"fare": 300
}
]
};
var g = {
nodes:[],
edges:[]
}
// Generate a random graph:
colors = [
'#617db4',
'#668f3c',
'#c6583e',
'#b956af'
];
sigma.utils.pkg('sigma.canvas.nodes');
sigma.canvas.nodes.border = function(node, context, settings) {
var prefix = settings('prefix') || '';
context.beginPath();
context.arc(
node[prefix + 'x']+15,
node[prefix + 'y'],
node[prefix + 'size']-2,
0,
Math.PI * 2,
true
);
//context.fillStyle = "orange";
context.strokeStyle = node.color || settings('defaultNodeColor');
//get the data from the group
//var data = d3.select(this).data();
context.stroke();
//context.fill();
context.font = "10px Arial";
context.fillStyle = "black";
context.strokeStyle = "black";
//write the text in the context
context.fillText(10,node[prefix + 'x']+15+ 10, node[prefix + 'size']-2-15);
// Adding a border
//context.lineWidth = node.borderWidth || 1;
//context.strokeStyle = node.borderColor || '#fff';
//context.stroke();
context.fillStyle = node.color || settings('defaultNodeColor');
context.beginPath();
context.arc(
node[prefix + 'x'],
node[prefix + 'y'],
node[prefix + 'size'],
0,
Math.PI * 2,
true
);
context.closePath();
context.fill();
};
for (var i = 0; i < graph.nodes.length; i++)
g.nodes.push({
id: graph.nodes[i]['city'],
label: graph.nodes[i]['city'],
x: Math.random(),
y: Math.random(),
size: 8,
color: colors[Math.floor(Math.random() * colors.length)]
});
for (var i = 0; i < graph.edges.length; i++)
g.edges.push({
id: graph.edges[i]['key'],
source: graph.edges[i]['source'],
target: graph.edges[i]['destination'],
size: 8,
label:graph.edges[i]['airlines'],
color: '#668e3e',
type:'curvedArrow'
});
s = new sigma({
graph: g,
renderer: {
container: document.getElementById('graph-container'),
type: 'canvas'
},
settings: {
edgeLabelSize: 'proportional',
minNodeSize: 1,
maxNodeSize: 10,
minEdgeSize: 0.1,
maxEdgeSize: 2,
enableEdgeHovering: true,
edgeHoverSizeRatio: 2,
defaultNodeType: 'border',
defaultNodeColor:"#fff",
mouseEnabled: true,
touchEnabled: true
}
});
//s.settings('autoRescale', false)
s.startForceAtlas2({worker: true, barnesHutOptimize: false});
s.stopForceAtlas2();
s.bind('rightClickNode', function(e) {
console.log(e.type, e.data.node.label, e.data.captor);
var name = 'New City'+Math.random();
s.graph.addNode({
id: name,
label: 'baai',
x: Math.random(),
y: Math.random(),
size: 8,
color: colors[Math.floor(Math.random() * colors.length)]
});
s.graph.addEdge({
id: name +Math.random(),
source: e.data.node.id,
target: name,
size: 8,
label:'bit'+Math.random(),
color: '#668e3e',
type:'curvedArrow'
});
// Edge with Already existing one
s.graph.addEdge({
id: name+Math.random(),
source: 'Huston',
target: name,
size: 8,
label:'New City'+Math.random(),
color: '#668e3e',
type:'curvedArrow'
});
setTimeout(function(){
s.refresh();
},1000)
});
</script>
</body>
</html>
尝试次数
单击节点时,我通过不断增加半径以圆形方式在其周围放置节点。在 JSFiddle 你可以看到这个。第一次点击很顺利,但在下一次点击时,它会在另一个圆圈中出现一个圆圈。我应该放多少半径才能让它看起来不像那样(如屏幕截图所示)?
第二次点击它相对定位,变成这个截图。但我想要实际定位而不是相对定位。
不使用位置 Math.random
,而是将位置设置为靠近被单击的节点,并让力布局负责将其移到正确的位置。所以,你应该改变这个:
s.graph.addNode({
x: Math.random(),
y: Math.random(),
到那个:
s.graph.addNode({
x: clickedNode.x + 2* Math.random() - 1,
y: clickedNode.y + 2* Math.random() - 1,
Fruchterman Reingold 力导向图布局 (algorithm summary) 将节点之间的力表示为弹簧连接钢响铃,并不断尝试在所有节点之间找到平衡。
它是 available as a plugin in Linkurious(Sigma.js 的分支)。这种布局可能正是您所需要的。
使用您自己的代码,并且仅以下依赖项:
sigma.plugins.animate
sigma.layouts.fruchtermanReingold
(来自 Linkurious 分支)
我得到了以下图形可视化:
为此,在您启动 Force Atlas 2 布局的地方,将其替换为以下内容:
sigma.layouts.fruchtermanReingold.configure(sigmaInstance, {easing: 'quadraticOut'});
sigma.layouts.fruchtermanReingold.start(sigmaInstance);
而且,最重要的部分是,在生成新节点或删除节点后,您必须重新运行 布局:
sigmaInstance.refresh();
sigma.layouts.fruchtermanReingold.start(sigmaInstance);
现在,一些提示
- 没有必要
setTimeout()
让它工作。请记住在每次图形更新后重新运行 力布局。
- 您的代码似乎重现了比您的屏幕截图显示的更丰富的可视化效果 - 具有弯曲的边缘、标签等。确保所有依赖项都已实际加载。
- 如果你确实使用弯曲的边缘,力布局仍然可以正常工作,但表示可能有点奇怪,所以考虑不使用它。
- Fruchterman-Reingold 布局可以配置自定义持续时间、重力、缓动方法等。阅读其文档以发挥其全部功能。
目标
我正在尝试在 sigma.js 中实现展开和折叠。右键单击任何节点,它会添加新节点并连接其边缘,但它会放置在随机位置。
我想在空闲 space 中添加节点,它们不应与其他节点碰撞或重叠。它应该随着动画慢慢展开,在空闲 space 区域展开,如 this example. Related.
代码
<!DOCTYPE html>
<html>
<head>
<title> Airlines Graph Render </title>
<script src="../build/sigma.min.js"></script>
<script src="../src/renderers/canvas/sigma.canvas.edges.curvedArrow.js"></script>
<script src="../plugins/sigma.layout.forceAtlas2/worker.js"></script>
<script src="../plugins/sigma.layout.forceAtlas2/supervisor.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/settings.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curve.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.def.js"></script>
<script src="../plugins/sigma.renderers.edgeLabels/sigma.canvas.edges.labels.curvedArrow.js"></script>
<style>
body,html{
width: 100%;
margin: 0px;
padding: 0px;
height: 100%
}
#graph-container {
width:100%;
height: 100%;
}
</style>
</head>
<body>
<div id="graph-container"></div>
<script >
var graph = {
"nodes": [
{
"city": "Dallas",
"area": 999,
"code": 214,
"country": "USA"
},
{
"city": "Austin",
"area": 1180,
"code": 512,
"country": "USA"
},
{
"city": "New York",
"area": 1214,
"code": 646,
"country": "USA"
},
{
"city": "Washington",
"area": 176,
"code": 564,
"country": "USA"
},
{
"city": "Atlanta",
"area": 342,
"code": 518,
"country": "USA"
},
{
"city": "Huston",
"area": 1625,
"code": 281,
"country": "USA"
},
{
"city": "Chicago",
"area": 606,
"code": 312,
"country": "USA"
},
{
"city": "London",
"area": 909,
"code": 312,
"country": "England"
}
],
"edges": [
{
"key": 1,
"source": "Dallas",
"destination": "Austin",
"distance": 200,
"airlines": "British Airways",
"fare": 220
},
{
"key": 2,
"source": "Austin",
"destination": "Dallas",
"distance": 200,
"airlines": "Lufthansa",
"fare": 120
},
{
"key": 3,
"source": "Washington",
"destination": "Dallas",
"distance": 1300,
"airlines": "Lufthansa",
"fare": 300
},
{
"key": 4,
"source": "Atlanta",
"destination": "Washington",
"distance": 600,
"airlines": "Lufthansa",
"fare": 600
},
{
"key": 5,
"source": "Washington",
"destination": "Atlanta",
"distance": 600,
"airlines": "KLM",
"fare": 400
},
{
"key": 6,
"source": "New York",
"destination": "Atlanta",
"distance": 300,
"airlines": "Qatar",
"fare": 1300
},
{
"key": 7,
"source": "Huston",
"destination": "Atlanta",
"distance": 800,
"airlines": "Indigo",
"fare": 400
},
{
"key": 8,
"source": "Atlanta",
"destination": "Huston",
"distance": 800,
"airlines": "Spicejet",
"fare": 600
},
{
"key": 9,
"source": "New York",
"destination": "Chicago",
"distance": 1000,
"airlines": "Air China",
"fare": 500
},
{
"key": 10,
"source": "Chicago",
"destination": "New York",
"distance": 1000,
"airlines": "Jet Airways",
"fare": 200
},
{
"key": 11,
"source": "Dallas",
"destination": "Chicago",
"distance": 900,
"airlines": "Lufthansa",
"fare": 1300
},
{
"key": 12,
"source": "Austin",
"destination": "Huston",
"distance": 160,
"airlines": "Lufthansa",
"fare": 240
},
{
"key": 13,
"source": "Dallas",
"destination": "New York",
"distance": 780,
"airlines": "Lufthansa",
"fare": 300
}
]
};
var g = {
nodes:[],
edges:[]
}
// Generate a random graph:
colors = [
'#617db4',
'#668f3c',
'#c6583e',
'#b956af'
];
sigma.utils.pkg('sigma.canvas.nodes');
sigma.canvas.nodes.border = function(node, context, settings) {
var prefix = settings('prefix') || '';
context.beginPath();
context.arc(
node[prefix + 'x']+15,
node[prefix + 'y'],
node[prefix + 'size']-2,
0,
Math.PI * 2,
true
);
//context.fillStyle = "orange";
context.strokeStyle = node.color || settings('defaultNodeColor');
//get the data from the group
//var data = d3.select(this).data();
context.stroke();
//context.fill();
context.font = "10px Arial";
context.fillStyle = "black";
context.strokeStyle = "black";
//write the text in the context
context.fillText(10,node[prefix + 'x']+15+ 10, node[prefix + 'size']-2-15);
// Adding a border
//context.lineWidth = node.borderWidth || 1;
//context.strokeStyle = node.borderColor || '#fff';
//context.stroke();
context.fillStyle = node.color || settings('defaultNodeColor');
context.beginPath();
context.arc(
node[prefix + 'x'],
node[prefix + 'y'],
node[prefix + 'size'],
0,
Math.PI * 2,
true
);
context.closePath();
context.fill();
};
for (var i = 0; i < graph.nodes.length; i++)
g.nodes.push({
id: graph.nodes[i]['city'],
label: graph.nodes[i]['city'],
x: Math.random(),
y: Math.random(),
size: 8,
color: colors[Math.floor(Math.random() * colors.length)]
});
for (var i = 0; i < graph.edges.length; i++)
g.edges.push({
id: graph.edges[i]['key'],
source: graph.edges[i]['source'],
target: graph.edges[i]['destination'],
size: 8,
label:graph.edges[i]['airlines'],
color: '#668e3e',
type:'curvedArrow'
});
s = new sigma({
graph: g,
renderer: {
container: document.getElementById('graph-container'),
type: 'canvas'
},
settings: {
edgeLabelSize: 'proportional',
minNodeSize: 1,
maxNodeSize: 10,
minEdgeSize: 0.1,
maxEdgeSize: 2,
enableEdgeHovering: true,
edgeHoverSizeRatio: 2,
defaultNodeType: 'border',
defaultNodeColor:"#fff",
mouseEnabled: true,
touchEnabled: true
}
});
//s.settings('autoRescale', false)
s.startForceAtlas2({worker: true, barnesHutOptimize: false});
s.stopForceAtlas2();
s.bind('rightClickNode', function(e) {
console.log(e.type, e.data.node.label, e.data.captor);
var name = 'New City'+Math.random();
s.graph.addNode({
id: name,
label: 'baai',
x: Math.random(),
y: Math.random(),
size: 8,
color: colors[Math.floor(Math.random() * colors.length)]
});
s.graph.addEdge({
id: name +Math.random(),
source: e.data.node.id,
target: name,
size: 8,
label:'bit'+Math.random(),
color: '#668e3e',
type:'curvedArrow'
});
// Edge with Already existing one
s.graph.addEdge({
id: name+Math.random(),
source: 'Huston',
target: name,
size: 8,
label:'New City'+Math.random(),
color: '#668e3e',
type:'curvedArrow'
});
setTimeout(function(){
s.refresh();
},1000)
});
</script>
</body>
</html>
尝试次数
单击节点时,我通过不断增加半径以圆形方式在其周围放置节点。在 JSFiddle 你可以看到这个。第一次点击很顺利,但在下一次点击时,它会在另一个圆圈中出现一个圆圈。我应该放多少半径才能让它看起来不像那样(如屏幕截图所示)?
第二次点击它相对定位,变成这个截图。但我想要实际定位而不是相对定位。
不使用位置 Math.random
,而是将位置设置为靠近被单击的节点,并让力布局负责将其移到正确的位置。所以,你应该改变这个:
s.graph.addNode({
x: Math.random(),
y: Math.random(),
到那个:
s.graph.addNode({
x: clickedNode.x + 2* Math.random() - 1,
y: clickedNode.y + 2* Math.random() - 1,
Fruchterman Reingold 力导向图布局 (algorithm summary) 将节点之间的力表示为弹簧连接钢响铃,并不断尝试在所有节点之间找到平衡。
它是 available as a plugin in Linkurious(Sigma.js 的分支)。这种布局可能正是您所需要的。
使用您自己的代码,并且仅以下依赖项:
sigma.plugins.animate
sigma.layouts.fruchtermanReingold
(来自 Linkurious 分支)
我得到了以下图形可视化:
为此,在您启动 Force Atlas 2 布局的地方,将其替换为以下内容:
sigma.layouts.fruchtermanReingold.configure(sigmaInstance, {easing: 'quadraticOut'});
sigma.layouts.fruchtermanReingold.start(sigmaInstance);
而且,最重要的部分是,在生成新节点或删除节点后,您必须重新运行 布局:
sigmaInstance.refresh();
sigma.layouts.fruchtermanReingold.start(sigmaInstance);
现在,一些提示
- 没有必要
setTimeout()
让它工作。请记住在每次图形更新后重新运行 力布局。 - 您的代码似乎重现了比您的屏幕截图显示的更丰富的可视化效果 - 具有弯曲的边缘、标签等。确保所有依赖项都已实际加载。
- 如果你确实使用弯曲的边缘,力布局仍然可以正常工作,但表示可能有点奇怪,所以考虑不使用它。
- Fruchterman-Reingold 布局可以配置自定义持续时间、重力、缓动方法等。阅读其文档以发挥其全部功能。