[英]D3, JSON, graph, force layout, data update, not complete redraw, drag
First sorry for my question, because I think this is because I don't understand very well D3 library, especially the working of Selections. 首先对我的问题感到抱歉,因为我认为这是因为我不太了解D3库,尤其是Selections的工作。
Here's what I want to do: 这是我想做的:
Here I don't want to do complete redraw of the graph; 在这里,我不想完全重画图形; I want the existing nodes and links stay where they are, and new nodes and links fly in to the scene.
我希望现有的节点和链接保持原样,而新的节点和链接飞入现场。 That's why I keep the instance of force layout outside of the drawGraph function (as global variable).
这就是为什么我将力布局实例保留在drawGraph函数之外(作为全局变量)的原因。
Task #1 done without a problem. 任务1顺利完成。
The problem is with task #2; 问题出在任务2上。 I could make the new node enter the scene..., but for some reason I cannot drag the existing nodes.
我可以让新节点进入场景...,但是由于某种原因,我无法拖动现有节点。 I can only drag the new nodes (Eduardo).
我只能拖动新节点(Eduardo)。
I have debugged it in Chrome, and saw this "var a" has 9 elements (after clicking). 我已经在Chrome中对其进行了调试,并看到此“ var a”具有9个元素(单击后)。 So, I suppose the function "force.layout" is supposed to be invoked by D3 for all those 9 elements.
因此,我假设D3应该为所有这9个元素调用函数“ force.layout”。 So, if I undestand correctly, I should've been able to drag all the 9 circles.
因此,如果我对它的理解不正确,那么我应该可以拖动所有9个圆圈。
But that's not the case, so something is wrong with my code. 但是事实并非如此,所以我的代码出了点问题。 Can anybody please point out where I got it wrong?
有人可以指出我做错了什么吗?
Here is the code. 这是代码。 And after that comes the JSON (two separate jsons, news.json and news3.json).
然后是JSON(两个独立的json,news.json和news3.json)。
Additional question: 附加问题:
I don't quite understand why this block (the key function) was executed 17 times (8 + 9) on the second invocation of drawGraph function (when the json is updated to news3.json)? 我不太明白为什么在第二次调用drawGraph函数时(当json更新为news3.json时)此块(键函数)执行了17次(8 + 9)吗? My expectation is 9 times.
我的期望是9倍。
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
console.log(d);
return d.source.index + '.' + d.target.index;
});
Thanks in advance!, 提前致谢!,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Force layout</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
<style type="text/css">
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><span id="type"></span></p>
<p><span id="name"></span></p>
</div>
<p id="refresh">Click on this text to update the chart with new data values (once).</p>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 300;
//http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file
var force = d3.layout.force()
.size([w, h])
.linkDistance([20])
.charge([-50]);
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
drawGraph = function(dataset) {
force
.nodes(dataset.nodes)
.links(dataset.edges)
.start();
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
console.log(d);
return d.source.index + '.' + d.target.index;
});
//Create edges as lines
var edges = lineSelections
.enter()
.append('line')
.style('stroke', function(d) {
if (d.target.type === 'publication') {
return 'red';
} else {
return 'blue';
}
})
.style('stroke-width', function(d) {
return d.weight;
});
var groups = svg.selectAll('g')
.data(dataset.nodes, function(d) {
console.log(d.name);
return d.name;
})
.enter()
.append('g');
var nodes = groups
.append('circle')
.attr('r', function(d) {
if (d.type === 'publication') {
var radius = d.weight / 6;
if (radius < 5) {
radius = 5;
}
return radius;
} else {
return d.weight * 3;
}
})
.style('fill', function(d, i) {
if (d.type === 'publication') {
return 'black';
} else {
return 'green';
}
});
var a = svg.selectAll('g circle');
a.call(force.drag);
//Create labels
var text = groups
.append('text')
.text(function(d) {
if (d.type === 'publication') {
return d.name;
} else {
return '';
}
})
.attr('x', function(d, i) {
d.x;
})
.attr('y', function(d) {
d.x;
})
.attr('font-family', 'sans-serif')
.attr('font-size', '24px')
.attr('fill', 'orange');
groups
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
//var hmm = d3.select(this).select('circle').attr('cx');
var xPosition = d3.select(this).select('circle').attr('cx');
var yPosition = d3.select(this).select('circle').attr('cy');
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#type")
.text(d.type);
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#name")
.text(d.name);
//Show the tooltip
//d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
})
//Every time the simulation "ticks", this will be called
force.on('tick', function() {
edges.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; });
nodes.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
//Update all labels
svg.selectAll('text')
.data(dataset.nodes)
.attr('x', function(d, i) {
return d.x;
})
.attr('y', function(d) {
return d.y;
});
});
}
d3.json('news.json', function(dataset) {
drawGraph(dataset);
});
d3.select("p")
.on("click", function() {
});
d3.select("#refresh")
.on("click", function() {
d3.json('news3.json', function(dataset) {
console.log(dataset);
drawGraph(dataset);
});
});
</script>
</body>
</html>
news.json news.json
{
"nodes": [
{ "name": "El Universal", "type": "publication", "weight": 10 },
{ "name": "Milenio", "type": "publication", "weight": 4},
{ "name": "Proceso", "type": "publication", "weight": 4},
{ "name": "Paco", "type": "person", "weight": 12},
{ "name": "Juan", "type": "person", "weight": 5},
{ "name": "Alberto", "type": "person", "weight": 5 },
{ "name": "Xochitl", "type": "person", "weight": 3 },
{ "name": "Reforma", "type": "publication", "weight": 2}
],
"edges": [
{ "source": 3, "target": 0, "weight": 9},
{ "source": 3, "target": 1, "weight": 3},
{ "source": 4, "target": 2, "weight": 4},
{ "source": 4, "target": 0, "weight": 1},
{ "source": 5, "target": 3, "weight": 5},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 6, "target": 7, "weight": 2},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 3, "target": 5, "weight": 4},
{ "source": 4, "target": 5, "weight": 1}
]
}
news3.json news3.json
{
"nodes": [
{ "name": "El Universal", "type": "publication", "weight": 10 },
{ "name": "Milenio", "type": "publication", "weight": 4},
{ "name": "Proceso", "type": "publication", "weight": 4},
{ "name": "Paco", "type": "person", "weight": 12},
{ "name": "Juan", "type": "person", "weight": 5},
{ "name": "Alberto", "type": "person", "weight": 5 },
{ "name": "Xochitl", "type": "person", "weight": 3 },
{ "name": "Reforma", "type": "publication", "weight": 2},
{ "name": "Eduardo", "type": "person", "weight": 2}
],
"edges": [
{ "source": 3, "target": 0, "weight": 9},
{ "source": 3, "target": 1, "weight": 3},
{ "source": 4, "target": 2, "weight": 4},
{ "source": 4, "target": 0, "weight": 1},
{ "source": 5, "target": 3, "weight": 5},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 6, "target": 7, "weight": 2},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 3, "target": 5, "weight": 4},
{ "source": 4, "target": 5, "weight": 1},
{ "source": 8, "target": 7, "weight": 2}
]
}
I got it working, after I modified the function assigned to the tick event on force.on('tick', ...);
在修改对
force.on('tick', ...);
的tick事件分配的函数后,它开始工作force.on('tick', ...);
. 。 Small modif: instead of using
groupSelection.selectAll('circle')
and groupSelection.selectAll('text')
, now I use groupSelection.select('circle')
and groupSelection.select('text')
. 小型修改:现在,我不再使用
groupSelection.selectAll('circle')
和groupSelection.selectAll('text')
,而是使用groupSelection.select('circle')
和groupSelection.select('text')
。
You can see a working demo here . 您可以在此处查看有效的演示。
Here is the code: 这是代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Force layout</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
<style type="text/css">
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><span id="type"></span></p>
<p><span id="name"></span></p>
</div>
<p id="refresh">Click on this text to update the chart with new data values (once).</p>
<script type="text/javascript">
//Width and height
var w = 300;
var h = 200;
//http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file
var force = d3.layout.force()
.size([w, h])
.linkDistance([20])
.charge([-50]);
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
drawGraph = function(dataset) {
force.nodes(dataset.nodes);
force.links(dataset.edges);
force.start();
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
return '.'.concat(d.source.name, '.', d.target.name);
});
lineSelections
.enter()
.append('line')
.style('stroke', function(d) {
if (d.target.type === 'publication') {
return 'red';
} else {
return 'blue';
}
})
.style('stroke-width', function(d) {
return d.weight;
});
var groupSelection = svg.selectAll('g')
.data(dataset.nodes, function(d) {
return d.name;
})
.call(force.drag);
var groups = groupSelection
.enter()
.append('g')
.call(force.drag);
groups
.append('circle')
.attr('r', function(d) {
if (d.type === 'publication') {
var radius = d.weight / 6;
if (radius < 5) {
radius = 5;
}
return radius;
} else {
return d.weight * 3;
}
})
.style('fill', function(d, i) {
if (d.type === 'publication') {
return 'black';
} else {
return 'green';
}
});
//Create labels
groups
.append('text')
.text(function(d) {
if (d.type === 'publication') {
return d.name;
} else {
return d.name;
}
})
.attr('x', function(d, i) {
d.x;
})
.attr('y', function(d) {
d.x;
})
.attr('font-family', 'sans-serif')
.attr('font-size', '14px')
.attr('fill', 'orange');
//Every time the simulation "ticks", this will be called
force.on('tick', function() {
lineSelections
.attr('x1', function(d) {
return d.source.x;
})
.attr('y1', function(d) {
return d.source.y;
})
.attr('x2', function(d) {
return d.target.x;
})
.attr('y2', function(d) {
return d.target.y;
});
groupSelection
.select('circle')
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
//Update all labels
groupSelection
.select('text')
.attr('x', function(d, i) {
return d.x;
})
.attr('y', function(d) {
return d.y;
});
});
}
d3.json('news.json', function(dataset) {
drawGraph(dataset);
});
d3.select("p")
.on("click", function() {
});
d3.select("#refresh")
.on("click", function() {
console.log('==================');
d3.json('news3.json', function(dataset) {
drawGraph(dataset);
});
});
</script>
</body>
</html>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.