链接永远不会出现

时间:2018-04-13 19:28:52

标签: javascript d3.js data-visualization

我正在开发一个主题建模项目,我尝试使用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;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

由于您的link选择是一组选择...

var link = g.selectAll('.link')
  .data(vis_link)
  .enter().append('g')
  .attr('class', 'link');

...您将x1x2y1y2属性应用于群组,而不是行。

一个简单的解决方案就是命名另一个选择,仅用于行:

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>