更改d3.js中某行的颜色

时间:2018-07-10 15:34:33

标签: javascript d3.js

我已经用javascript中的d3.js实现了一个网络,我想根据收到的json将链接的颜色从灰色更改为红色。 JSON显示将要连接的源节点和目标节点。节点体系结构的架构如下:

enter image description here

现在,当接收到JSON时,我将解析数据,并使用以下代码添加新行,将源节点和目标节点连接到红色:

       svg.selectAll()
          .data(data.links)
          .enter()
          .append("line")
          .attr("x1", function(d) { return nodes[source].x; })
          .attr("y1", function(d) { return nodes[source].y; })
          .attr("x2", function(d) { return nodes[target].x; })
          .attr("y2", function(d) { return nodes[target].y; })
          .attr("stroke","#b72b34")
          .attr("stroke-width", 2)

然后,在2秒后,我使用相同的代码再次将线条的颜色更改为灰色。 还有另一种更简单的方法吗?

我现在得到的结果是: enter image description here

您可以看到,用户可以看到过去添加的行,尽管这不是所需的图像。我只想更改现有链接的颜色(如架构1所示)。

谢谢。

我的代码:

var nodes = [
 { x:   width/2, y: height/4, id: 0, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-150, id: 1, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-150, id: 2, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-50, id: 3, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-50, id: 4, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Inkjet-Printer-icon.png"}
  ];


  var links = [
  { source: 1, target: 2 },
  { source: 1, target: 3 },
  { source: 1, target: 4 },
  { source: 1, target: 0 },
  { source: 2, target: 3 },
  { source: 2, target: 1 },
  { source: 2, target: 4 },
  { source: 2, target: 0 },
  { source: 3, target: 4 },
  { source: 3, target: 1 },
  { source: 3, target: 2 },
  { source: 3, target: 0 },
  { source: 4, target: 1 },
  { source: 4, target: 2 },
  { source: 4, target: 3 },
  { source: 4, target: 0 },
  { source: 0, target: 1 },
  { source: 0, target: 2 },
  { source: 0, target: 3 },
  { source: 0, target: 4 },
  ];

  svg.selectAll()
     .data(links)
  .enter()
  .append("line")
   .attr("x1", function(d) { return nodes[d.source].x; })
   .attr("y1", function(d) { return nodes[d.source].y; })
   .attr("x2", function(d) { return nodes[d.target].x; })
   .attr("y2", function(d) { return nodes[d.target].y; })
   .attr("stroke-width", function (d) { return Math.sqrt(d.value); })
   .attr("stroke","#f6f6f6")
   svg.selectAll()
    .data(nodes)
    .enter()
    .append("image")
    .attr("x", function(d) { return d.x - 30/2; })  
    .attr("y", function(d) { return d.y - 30/2; })  
    .attr("width", 45)
    .attr("height", 45)
    .attr("xlink:href",function(d) { return d.url; })

    setInterval(function(test){
       var url = "http://..." 
       d3.json(url, function(error, data) {
       for (var i = 0; i < data.links.length; i++) {
         source = findNode(data.links[i].source);
         target = findNode(data.links[i].target);
            svg.selectAll()
              .data(data.links)
              .enter()
              .append("line")
              .attr("x1", function(d) { return nodes[source].x; })
              .attr("y1", function(d) { return nodes[source].y; })
              .attr("x2", function(d) { return nodes[target].x; })
              .attr("y2", function(d) { return nodes[target].y; })
              .attr("stroke","#b72b34")
              .attr("stroke-width", function (d) { return Math.sqrt(d.value); 
               })
          ;

  }
   });
   }, 5000);

2 个答案:

答案 0 :(得分:2)

您当前每次更新图表时都会添加新行:

svg.selectAll()
   .data(data.links)
   .enter()
   .append("line")

svg.selectAll()是一个空选择,与svg.selectAll(null)相同。因此,将在DOM中为数据数组中每项更新的项目创建一个项目。一段时间后,这会使速度变慢。

我们可以用svg.selectAll("circle")选择圆以进行更新(或退出/输入旧行/新行),但是新数据数组将按照其索引顺序(而不是数据对)分配给现有行。我们可以使用.selectAll().data()the second argument for .data())为每个元素做一个键,但这必须是一个字符串。

我建议不要使用键,而是使用每行的ID(表示最初添加这些行)来代表其源和目标:

 .attr("id", function(d) { return "link-"+d.source+"-"+d.target; })

然后,当我们处理json数据以更新图形时,我们选择适当的行。选择之后,我们将应用样式。使用转换时的延迟,我们可以确保两秒钟后完成返回正常状态的转换:

newData.links.forEach(function(d) {
    d3.select("#link-"+d.source+"-"+d.target)
       .attr("stroke","red")
       .transition()
       .delay(1500)
       .duration(500)
       .attr("stroke","#f6f6f6")
})

这部分代码适合您的超时功能:

var width = 600;
var height = 400;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);

var nodes = [
 { x:   width/2, y: height/4, id: 0, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-150, id: 1, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-150, id: 2, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-50, id: 3, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-50, id: 4, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Inkjet-Printer-icon.png"}
  ];

 
  var links = [
  { source: 1, target: 2 },
  { source: 1, target: 3 },
  { source: 1, target: 4 },
  { source: 1, target: 0 },
  { source: 2, target: 3 },
  { source: 2, target: 1 },
  { source: 2, target: 4 },
  { source: 2, target: 0 },
  { source: 3, target: 4 },
  { source: 3, target: 1 },
  { source: 3, target: 2 },
  { source: 3, target: 0 },
  { source: 4, target: 1 },
  { source: 4, target: 2 },
  { source: 4, target: 3 },
  { source: 4, target: 0 },
  { source: 0, target: 1 },
  { source: 0, target: 2 },
  { source: 0, target: 3 },
  { source: 0, target: 4 },
  ];

svg.selectAll()
     .data(links)
  .enter()
  .append("line")
   .attr("x1", function(d) { return nodes[d.source].x; })
   .attr("y1", function(d) { return nodes[d.source].y; })
   .attr("x2", function(d) { return nodes[d.target].x; })
   .attr("y2", function(d) { return nodes[d.target].y; })
   .attr("stroke-width", function (d) { return Math.sqrt(d.value); })
   .attr("id", function(d) { return "link-"+d.source+"-"+d.target; })
   .attr("stroke","#f6f6f6")
   
   svg.selectAll()
    .data(nodes)
    .enter()
    .append("image")
    .attr("x", function(d) { return d.x - 45/2; })  
    .attr("y", function(d) { return d.y - 45/2; })  
    .attr("width", 45)
    .attr("height", 45)
    .attr("xlink:href",function(d) { return d.url; })

    setInterval(function(){
         // randomly links instead of calling external file:
         var data = links.filter(function(d) { if (Math.random() < 0.5) return d; })
		
        data.forEach(function(d) {
          d3.select("#link-"+d.source+"-"+d.target)
              .attr("stroke","red")
              .transition()
              .delay(1500)
              .duration(500)
              .attr("stroke","#f6f6f6")
          })

   }, 3000);  // sped up for demonstration
<script src="https://d3js.org/d3.v3.min.js"></script>

为便于比较,以下是提供给.data()的密钥作为替代方法。为此,我们将目标和源转换为字符串,就像对id所做的一样,但是将其用作.data()中的键。这使我们可以跳过每个循环。这是更规范的d3方法

var width = 600;
var height = 400;

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height);

var nodes = [
 { x:   width/2, y: height/4, id: 0, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-150, id: 1, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-150, id: 2, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   60, y: height-50, id: 3, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Computer-icon.png"},
 { x:   width-100, y: height-50, id: 4, url: "http://icons.iconarchive.com/icons/icons-land/vista-hardware-devices/128/Inkjet-Printer-icon.png"}
  ];

 
  var links = [
  { source: 1, target: 2 },
  { source: 1, target: 3 },
  { source: 1, target: 4 },
  { source: 1, target: 0 },
  { source: 2, target: 3 },
  { source: 2, target: 1 },
  { source: 2, target: 4 },
  { source: 2, target: 0 },
  { source: 3, target: 4 },
  { source: 3, target: 1 },
  { source: 3, target: 2 },
  { source: 3, target: 0 },
  { source: 4, target: 1 },
  { source: 4, target: 2 },
  { source: 4, target: 3 },
  { source: 4, target: 0 },
  { source: 0, target: 1 },
  { source: 0, target: 2 },
  { source: 0, target: 3 },
  { source: 0, target: 4 },
  ];

svg.selectAll()
  .data(links, function(d) { return d.source+"-"+d.target })
  .enter()
  .append("line")
   .attr("x1", function(d) { return nodes[d.source].x; })
   .attr("y1", function(d) { return nodes[d.source].y; })
   .attr("x2", function(d) { return nodes[d.target].x; })
   .attr("y2", function(d) { return nodes[d.target].y; })
   .attr("stroke-width", function (d) { return Math.sqrt(d.value); })
   .attr("stroke","#f6f6f6")
   
   svg.selectAll()
    .data(nodes)
    .enter()
    .append("image")
    .attr("x", function(d) { return d.x - 45/2; })  
    .attr("y", function(d) { return d.y - 45/2; })  
    .attr("width", 45)
    .attr("height", 45)
    .attr("xlink:href",function(d) { return d.url; })

    setInterval(function(){
		// randomly links instead of calling external file:
     var data = links.filter(function(d) { if (Math.random() < 0.5) return d; })
		
     d3.selectAll("line")
       .data(data, function(d) { return d.source+"-"+d.target; })
       .attr("stroke","red")
       .transition()
       .delay(1500)
       .duration(500)
       .attr("stroke","#f6f6f6")


   }, 3000);  // sped up for demonstration
<script src="https://d3js.org/d3.v3.min.js"></script>

答案 1 :(得分:0)

您可以将行选择缓存在变量中,以在超时时重新使用它。看看下面的代码。我还添加了一个可选的300ms过渡。

const redLine = svg.selectAll()
  .data(data.links)
  .enter()
  .append("line")
  .attr("x1", function(d) { return nodes[source].x; })
  .attr("y1", function(d) { return nodes[source].y; })
  .attr("x2", function(d) { return nodes[target].x; })
  .attr("y2", function(d) { return nodes[target].y; })
  .attr("stroke","#b72b34")
  .attr("stroke-width", 2)

// drop this code inside setTimeout() instead of the whole block of code from 
// above that you repeat
redLine
  .transition(300)
  .attr("stroke","grey")
  .attr("stroke-width", 1)