我正在开发一个主题建模项目,我尝试使用D3.js来显示结果。遗憾的是,这是我第一次体验D3.js
我尝试将主要主题链接到他们的子主题,所以我按照教程关于如何使用"强制模拟"在D3。现在节点看起来非常好,但链接永远不会出现。
与本教程相比,唯一的区别在于此项目我必须修复x轴,因为所有主题都绑定到一个时间段。
此外,在调用" function restart()"后,我在小气泡中的文字消失了。
请给我一些建议。
data= [
{id: "Topic1", date: "2017-08-21", name: "Topic1", count: .4, subtopics: ["sub1", "sub2", "sub3", "sub5"]},
{id: "Topic2", date: "2017-08-23", name: "Topic2", count: 1, subtopics: ["sub3", "sub6", "sub7", "sub8"]},
{id: "Topic3", date: "2017-08-25", name: "Topic3",count: 2, subtopics: ["sub7", "sub9"]},
{id: "Topic4", date: "2017-08-27", name: "Topic4", count: 2, subtopics: ["sub8"]},
{id: "sub1",date:"2017-08-21", name:"sub1", count: .1, subtopics: []},
{id: "sub2",date:"2017-08-22", name:"sub2", count: .2, subtopics: []},
{id: "sub3",date:"2017-08-22", name:"sub3", count: .2, subtopics: []},
{id: "sub4",date:"2017-08-28", name:"sub4", count: .1, subtopics: []},
{id: "sub5",date:"2017-08-20", name:"sub5", count: .2, subtopics: []},
{id: "sub6",date:"2017-08-23", name:"sub6", count: .1, subtopics: []},
{id: "sub7",date:"2017-08-24", name:"sub7", count: .3, subtopics: []},
{id: "sub8",date:"2017-08-24", name:"sub8", count: .1, subtopics: []},
{id: "sub9",date:"2017-08-25", name:"sub9", count: .1, subtopics: []},
{id: "sub10",date:"2017-08-27", name:"sub10", count: .1, subtopics: []},
{id: "sub11",date:"2017-08-29", name:"sub11", count: .1, subtopics: []},
{id: "sub12",date:"2017-08-30", name:"sub12", count: .4, subtopics: []},
]
var vis_node = [];
var vis_link = [];
var r = d3.scaleSqrt()
.domain([0, d3.max(data, function (d) {
return d.count;
})])
.range([0, 65]);
var margin = {top: 50, right: 20, bottom: 100, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//map elements to time
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
//separete big topic and subtopics and setup the visualization data
var parents_node = data.filter(function(d){return d.subtopics.length != 0;});
var child_node = data.filter(function(d){return d.subtopics.length == 0;});
vis_node = data;
//link all the big topic with common subtopic
for(i = 0;i<data.length;i++){
var tmps = data[i].subtopics
for(j = 0; j < tmps.length; j++){
for(k = i+1; k < data.length;k++){
if(data[k].subtopics.includes(tmps[j])){
vis_link.push({source: data[i], target: data[k]})
continue;
}
}
}
}
//link all the big topic with its subtopic
for(i = 0;i<parents_node.length;i++){
for(j = 0;j<child_node.length;j++){
if(parents_node[i].subtopics.includes(child_node[j].name)){
vis_link.push({source: parents_node[i], target: child_node[j]});
}
}
}
//setup a force field for the d3
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(280))
.force("link", d3.forceLink().id(function(d) { return d.index }))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY(0.0001))
.force("x", d3.forceX(0.0001))
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom),
g = svg.append('g')
.attr('transform','translate(' + margin.left + ',' + margin.top + ')');
var formatNumber = d3.format('');
var x = d3.scaleTime()
.range([0, width]);
x.domain(d3.extent(data, function(d) { return d.date; }));
//begin to render the circle
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m-%d")))
var node = g.selectAll('.node')
.data(vis_node)
.enter().append('g')
.attr("class", "node");
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append('circle')
.attr('r', function(d) {return Math.max(16, r(d.count)); })
.style("stroke", function(d){
if(d.subtopics.length == 0){
return "blue"
}
else{
return "pink"
}
})
.style("fill", "transparent");
node.append('text')
.text(function(d){return d.name})
.attr("text-anchor", "middle")
.style('fill', function(d){
if(d.subtopics.length == 0){
return "darkblue"
}
else{
return "darkred"
}
})
.style('font-size','20px')
.attr("pointer-events", "none");
node.on("click", function(d){
var subs = d.subtopics
child_node.forEach(function(d){
if(subs.includes(d.name)){
if(vis_node.includes(d)){
var index = vis_node.indexOf(d)
vis_node.splice(index, 1)
}
else{
vis_node.push(d);
}
}
})
restart();
});
//begin to render a link
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class','link')
link.append('line')
.attr("stroke","black")
//when click on the big topic call restart function to redraw everything
function restart(){
vis_node.forEach(function(d){console.log(d.name)})
node = node.data(vis_node)
node.exit().remove()
node = node.enter().append('circle')
.attr('r', function(d) {return Math.max(16, r(d.count)); })
.style("stroke", function(d){
if(d.subtopics.length == 0){
return "blue"
}
else{
return "pink"
}
})
.style("fill", "transparent")
.merge(node);
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
node.append('text')
.text(function(d){return d.name})
.attr("text-anchor", "middle")
.style('fill', function(d){
if(d.subtopics.length == 0){
return "darkblue"
}
else{
return "darkred"
}
})
.style('font-size','20px')
.attr("pointer-events", "none");
simulation.nodes(vis_node);
simulation.alphaTarget(0.3).restart();
}
//I think something wrong here
var ticked = function() {
node.attr("transform", function (d) {
return "translate(" + x(d.date) + "," + d.y + ")";
})
link.attr("x1", function (d) {return x(d.source.date); })
.attr("y1", function (d) {return d.source.y;})
.attr("x2", function (d) {return x(d.target.date)})
.attr("y2", function (d) {return d.target.y;});
}
//add link and node to the force field
simulation.force("link").links(vis_link);
simulation.nodes(vis_node);
simulation.on("tick", ticked);
//drag related functions
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
&#13;
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Topic Explorer</title>
<base href="/">
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" href="./styles/simple-style.css">
</head>
<body style="margin:10px 0">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./scripts/test.js"></script>
</body>
</html>
&#13;
答案 0 :(得分:1)
由于您的link
选择是一组选择...
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class', 'link');
...您将x1
,x2
,y1
和y2
属性应用于群组,而不是行。
一个简单的解决方案就是命名另一个选择,仅用于行:
var line = link.append('line')
.attr("stroke", "black");
以下是您修改后的代码:
data = [{
id: "Topic1",
date: "2017-08-21",
name: "Topic1",
count: .4,
subtopics: ["sub1", "sub2", "sub3", "sub5"]
},
{
id: "Topic2",
date: "2017-08-23",
name: "Topic2",
count: 1,
subtopics: ["sub3", "sub6", "sub7", "sub8"]
},
{
id: "Topic3",
date: "2017-08-25",
name: "Topic3",
count: 2,
subtopics: ["sub7", "sub9"]
},
{
id: "Topic4",
date: "2017-08-27",
name: "Topic4",
count: 2,
subtopics: ["sub8"]
},
{
id: "sub1",
date: "2017-08-21",
name: "sub1",
count: .1,
subtopics: []
},
{
id: "sub2",
date: "2017-08-22",
name: "sub2",
count: .2,
subtopics: []
},
{
id: "sub3",
date: "2017-08-22",
name: "sub3",
count: .2,
subtopics: []
},
{
id: "sub4",
date: "2017-08-28",
name: "sub4",
count: .1,
subtopics: []
},
{
id: "sub5",
date: "2017-08-20",
name: "sub5",
count: .2,
subtopics: []
},
{
id: "sub6",
date: "2017-08-23",
name: "sub6",
count: .1,
subtopics: []
},
{
id: "sub7",
date: "2017-08-24",
name: "sub7",
count: .3,
subtopics: []
},
{
id: "sub8",
date: "2017-08-24",
name: "sub8",
count: .1,
subtopics: []
},
{
id: "sub9",
date: "2017-08-25",
name: "sub9",
count: .1,
subtopics: []
},
{
id: "sub10",
date: "2017-08-27",
name: "sub10",
count: .1,
subtopics: []
},
{
id: "sub11",
date: "2017-08-29",
name: "sub11",
count: .1,
subtopics: []
},
{
id: "sub12",
date: "2017-08-30",
name: "sub12",
count: .4,
subtopics: []
},
]
var vis_node = [];
var vis_link = [];
var r = d3.scaleSqrt()
.domain([0, d3.max(data, function(d) {
return d.count;
})])
.range([0, 65]);
var margin = {
top: 50,
right: 20,
bottom: 100,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//map elements to time
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
//separete big topic and subtopics and setup the visualization data
var parents_node = data.filter(function(d) {
return d.subtopics.length != 0;
});
var child_node = data.filter(function(d) {
return d.subtopics.length == 0;
});
vis_node = data;
//link all the big topic with common subtopic
for (i = 0; i < data.length; i++) {
var tmps = data[i].subtopics
for (j = 0; j < tmps.length; j++) {
for (k = i + 1; k < data.length; k++) {
if (data[k].subtopics.includes(tmps[j])) {
vis_link.push({
source: data[i],
target: data[k]
})
continue;
}
}
}
}
//link all the big topic with its subtopic
for (i = 0; i < parents_node.length; i++) {
for (j = 0; j < child_node.length; j++) {
if (parents_node[i].subtopics.includes(child_node[j].name)) {
vis_link.push({
source: parents_node[i],
target: child_node[j]
});
}
}
}
//setup a force field for the d3
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(280))
.force("link", d3.forceLink().id(function(d) {
return d.index
}))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY(0.0001))
.force("x", d3.forceX(0.0001))
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom),
g = svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var formatNumber = d3.format('');
var x = d3.scaleTime()
.range([0, width]);
x.domain(d3.extent(data, function(d) {
return d.date;
}));
//begin to render the circle
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m-%d")))
var node = g.selectAll('.node')
.data(vis_node)
.enter().append('g')
.attr("class", "node");
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append('circle')
.attr('r', function(d) {
return Math.max(16, r(d.count));
})
.style("stroke", function(d) {
if (d.subtopics.length == 0) {
return "blue"
} else {
return "pink"
}
})
.style("fill", "transparent");
node.append('text')
.text(function(d) {
return d.name
})
.attr("text-anchor", "middle")
.style('fill', function(d) {
if (d.subtopics.length == 0) {
return "darkblue"
} else {
return "darkred"
}
})
.style('font-size', '20px')
.attr("pointer-events", "none");
node.on("click", function(d) {
var subs = d.subtopics
child_node.forEach(function(d) {
if (subs.includes(d.name)) {
if (vis_node.includes(d)) {
var index = vis_node.indexOf(d)
vis_node.splice(index, 1)
} else {
vis_node.push(d);
}
}
})
restart();
});
//begin to render a link
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class', 'link')
var line = link.append('line')
.attr("stroke", "black")
//when click on the big topic call restart function to redraw everything
function restart() {
vis_node.forEach(function(d) {
console.log(d.name)
})
node = node.data(vis_node)
node.exit().remove()
node = node.enter().append('circle')
.attr('r', function(d) {
return Math.max(16, r(d.count));
})
.style("stroke", function(d) {
if (d.subtopics.length == 0) {
return "blue"
} else {
return "pink"
}
})
.style("fill", "transparent")
.merge(node);
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
node.append('text')
.text(function(d) {
return d.name
})
.attr("text-anchor", "middle")
.style('fill', function(d) {
if (d.subtopics.length == 0) {
return "darkblue"
} else {
return "darkred"
}
})
.style('font-size', '20px')
.attr("pointer-events", "none");
simulation.nodes(vis_node);
simulation.alphaTarget(0.3).restart();
}
//I think something wrong here
var ticked = function() {
node.attr("transform", function(d) {
return "translate(" + x(d.date) + "," + d.y + ")";
})
line.attr("x1", function(d) {
return x(d.source.date);
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return x(d.target.date)
})
.attr("y2", function(d) {
return d.target.y;
});
}
//add link and node to the force field
simulation.force("link").links(vis_link);
simulation.nodes(vis_node);
simulation.on("tick", ticked);
//drag related functions
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Topic Explorer</title>
<base href="/">
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="./styles/simple-style.css">
</head>
<body style="margin:10px 0">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./scripts/test.js"></script>
</body>
</html>