d3.js:如何绘制和突出显示此关系图中的链接?

时间:2013-09-03 22:01:13

标签: d3.js relationship

我一直试图解决这个图形问题已经持续了几个星期......对于D3.js来说还是一个新手,所以看起来他们可能很简单的事情仍然无法解决。

以下是我正在尝试做的事情的说明:

relationship Chart

目标

  1. 我想展示行业节点/大小节点和行业节点之间的关系 产品节点。

  2. 当我悬停Product节点时,我想突出显示 每个的链接,来源(行业或规模)和目标(产品) 相关关系。

  3. 当我悬停行业或规模节点时,我想突出显示所有相关产品的链接。

  4. 问题

    1. 如何绘制链接?我知道它以某种方式涉及使用d3.map ...但无法弄明白。

    2. 如何突出显示节点和链接(目标2和3)?

    3. 如果有更好,更有效的方法来获得这种布局和行为,请告诉我 - 努力学习技巧!

    4. 小提琴从简化的数据集渲染基本布局: http://jsfiddle.net/9hGbD/

      数据目前看起来像这样:

          var data = {
          "Product": [
              {
                  "type": "product",
                  "name": "Product 1"
              },
              {
                  "type": "product",
                  "name": "Product 2"
              },
              {
                  "type": "product",
                  "name": "Product 3"
              },
              {
                  "type": "product",
                  "name": "Product 4"
              },
              {
                  "type": "product",
                  "name": "Product 5"
              }
          ],
          "Industry": [
      
              {
                  "type": "industry",
                  "name": "Industry 1"
              },
              {
                  "type": "industry",
                  "name": "Industry 2"
              },
              {
                  "type": "industry",
                  "name": "Industry 3"
              },
              {
                  "type": "industry",
                  "name": "Industry 4"
              },
              {
                  "type": "industry",
                  "name": "Industry 5"
              }
          ],
          "Size": [
              {
                  "type": "size",
                  "name": "Size 1"
              },
              {
                  "type": "size",
                  "name": "Size 2"
              },
              {
                  "type": "size",
                  "name": "Size 3"
              },
              {
                  "type": "size",
                  "name": "Size 4"
              },
              {
                  "type": "size",
                  "name": "Size 5"
              }
          ],
          "links": [
              {
                  "source": "Industry 1",
                  "target": "Product 1"
              },
              {
                  "source": "Industry 3",
                  "target": "Product 1"
              },
              {
                  "source": "Industry 5",
                  "target": "Product 1"
              },
              {
                  "source": "Industry 2",
                  "target": "Product 2"
              },
              ...etc..
          ]
      };
      

      我正在使用的javascript 看起来像这样:

      function renderRelationshipGraph(){
      
              var width = 800,
                  boxWidth = 200,
                  boxHeight = 20,
                  gap = 4,
                  margin = {top: 16, right: 16, bottom: 16, left: 16},
                  height = (data.Product.length * (boxHeight + gap)) + margin.top + margin.bottom;
      
              var pNodes = [];
              var iNodes = [];
              var sNodes = [];
              var links = [];
      
              data.Product.forEach(function(d, i) {
                  d.x = ((width-margin.left-margin.right)/3)/2 - boxWidth/2;
                  d.y = margin.top + (boxHeight+ 4)*i;
                  pNodes.push(d);
              });
      
              data.Industry.forEach(function(d, i) {
                  d.x = 0;
                  d.y = margin.top + (boxHeight+ 4)*i; 
                  iNodes.push(d);
              });
      
              data.Size.forEach(function(d, i) {
                  d.x = ((width-margin.left-margin.right)/3) - boxWidth;
                  d.y = margin.top + (boxHeight+ 4)*i; 
                  sNodes.push(d);
              });
      
              var svg = d3.select("#graph").append("svg")
                      .attr("width", width)
                      .attr("height", height)
                      .append("g");
      
              svg.append("g")
                      .attr("class", "industries");
      
      
              svg.append("g")
                      .attr("class", "products")
                      .attr("transform", "translate("+ (width-margin.left-margin.right)/3 + ", 0)"); 
      
              svg.append("g")
                      .attr("class", "sizes")
                      .attr("transform", "translate("+ 2*((width-margin.left-margin.right)/3) + ", 0)"); 
      
              var products = svg.select(".products");
              var product = products.selectAll("g")
                      .data(pNodes)
                      .enter()
                      .append("g")
                      .attr("class", "unit");
      
                      product.append("rect")
                      .attr("x", function(d) {return d.x;})
                      .attr("y", function(d) {return d.y;})
                      .attr("width", boxWidth)
                      .attr("height", boxHeight)
                      .attr("class", "product")
                      .attr("rx", 6)
                      .attr("ry", 6)
                      .on("mouseover", function() { d3.select(this).classed("active", true); })
                      .on("mouseout", function() { d3.select(this).classed("active", false); });
      
                      product.append("text")
                      .attr("class", "label")
                      .attr("x", function(d) {return d.x + 14;})
                      .attr("y", function(d) {return d.y + 15;})
                      .text(function(d) {return d.name;});
      
              var industries = svg.select(".industries");
              var industry = industries.selectAll("g")
                      .data(iNodes)
                      .enter()
                      .append("g")
                      .attr("class", "unit");
      
                      industry.append("rect")
                      .attr("x", function(d) {return d.x;})
                      .attr("y", function(d) {return d.y;})
                      .attr("width", boxWidth)
                      .attr("height", boxHeight)
                      .attr("class", "industry")
                      .attr("rx", 6)
                      .attr("ry", 6)
                      .on("mouseover", function() { d3.select(this).classed("active", true); })
                      .on("mouseout", function() { d3.select(this).classed("active", false); });
      
                      industry.append("text")
                      .attr("class", "label")
                      .attr("x", function(d) {return d.x + 14;})
                      .attr("y", function(d) {return d.y + 15;})
                      .text(function(d) {return d.name;});
      
              var sizes = svg.select(".sizes");
              var size = sizes.selectAll("g")
                      .data(sNodes)
                      .enter()
                      .append("g")
                      .attr("class", "unit");
      
                      size.append("rect")
                      .attr("x", function(d) {return d.x;})
                      .attr("y", function(d) {return d.y;})
                      .attr("width", boxWidth)
                      .attr("height", boxHeight)
                      .attr("class", "size")
                      .attr("rx", 6)
                      .attr("ry", 6)
                      .on("mouseover", function() { d3.select(this).classed("active", true); })
                      .on("mouseout", function() { d3.select(this).classed("active", false); });
      
                      size.append("text")
                      .attr("class", "label")
                      .attr("x", function(d) {return d.x + 14;})
                      .attr("y", function(d) {return d.y + 15;})
                      .text(function(d) {return d.name;});
          }
      
          renderRelationshipGraph();
      

      感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

好的,所以我试着让这个例子起作用,因为我遇到了同样的问题。为了后代,我也在这里回答。也许有人会遇到同样的问题或找到一个更简单的解决方案 我是javascript的新手(过去一周试图学习一点)所以这可能不是最好的解决方案。 jsfiddle中提供了文件和结果。我不会提供整个代码,只会指出更改。

  • 为了能够添加任意数量的列,我更改了json文件,并将type替换为级别lvl。我还将IndustriyProductsize替换为名为Nodes的类别。

  • javascript文件中创建的svg中的每个矩形和线都使用id进行设置,以便稍后可以引用它(更改颜色时)。

相关代码是

  data.Nodes.forEach(function (d, i) {
      d.x = margin.left + d.lvl * (boxWidth + gap.width);
      d.y = margin.top + (boxHeight + gap.height) * count[d.lvl];
      d.id = "n" + i;
      count[d.lvl] += 1;
      Nodes.push(d);
  });

  data.links.forEach(function (d) {
      links.push({
         source: find(d.source),
         target: find(d.target),
         id: "l" + find(d.source).id + find(d.target).id
      });
  });

此处使用的方法find是一个函数,用于查找传递给它的名称的节点。

  • mouseovermouseout事件更新如下:

     .on("mouseover", function () {
         mouse_action(d3.select(this).datum(), true, true);
     })
     .on("mouseout", function () {
         mouse_action(d3.select(this).datum(), false, true);
     });
    

它使用一种方法mouse_action,它接受​​起始节点及其将处于的状态(活动或非活动)。在此方法中,我们访问每个链接,处理它并更改其状态。该方法在鼠标输入的节点上以两个方向(左和右)遍历,并且仅在其他节点上向左或向右遍历。

function mouse_action(val, stat, direction) {
    "use strict";
    d3.select("#" + val.id).classed("active", stat);

    links.forEach(function (d) {
        if (direction == "root") {
            if (d.source.id === val.id) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                if (d.target.lvl < val.lvl)
                    mouse_action(d.target, stat, "left");
                else if (d.target.lvl > val.lvl)
                    mouse_action(d.target, stat, "right");
            }
            if (d.target.id === val.id) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                if (direction == "root") {
                    if(d.source.lvl < val.lvl)
                        mouse_action(d.source, stat, "left");
                    else if (d.source.lvl > val.lvl)
                        mouse_action(d.source, stat, "right");
                }
            }
        }else if (direction == "left") {
            if (d.source.id === val.id && d.target.lvl < val.lvl) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                mouse_action(d.target, stat, direction);
            }
            if (d.target.id === val.id && d.source.lvl < val.lvl) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                mouse_action(d.source, stat, direction);
            }
        }else if (direction == "right") {
            if (d.source.id === val.id && d.target.lvl > val.lvl) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                mouse_action(d.target, stat, direction);
            }
            if (d.target.id === val.id && d.source.lvl > val.lvl) {
                d3.select("#" + d.id).moveToFront().classed("activelink", stat); // change link color
                d3.select("#" + d.id).moveToFront().classed("link", !stat); // change link color
                mouse_action(d.source, stat, direction);
            }
        }
    });
}