如何使用 D3 在鼠标悬停的图形中缩放每个节点(表示为饼图)

How to scale each node, represented as a pie chart, in a graph on mouse over using D3

我正在 D3 中进行图表可视化,其中每个节点代表一个从事多个项目并与多人协作的人。因此,我将节点自定义为饼图,其中部分饼​​图表示人员分配给不同项目的时间百分比。饼图的半径根据赋予每个节点的一些权重而变化。

由于这种不同的半径,一些节点非常小,以至于无法将它们视为饼图。所以,我想创建一个鼠标悬停效果,可以放大鼠标悬停在节点上的节点。 我的目的不是为饼图的每个部分制作动画,而是为整个饼图制作动画,就好像它是一个简单的圆圈一样。

我为悬停饼图的弧线添加动画的代码不起作用。当我将鼠标悬停在一个节点上时,出现以下错误

Error: <path> attribute d: Expected number, "MNaN,NaNA12.80000…".
(anonymous) @ d3.v3.min.js:5
a @ d3.v3.min.js:3
Rn @ d3.v3.min.js:1
Tn @ d3.v3.min.js:1

这是我的代码片段 -

<!DOCTYPE html>
<html lang="en">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
    .link {
      fill: none;
      stroke: #000000;
      stroke-width: 1.5px;
      stroke-opacity: 0.8;
    }

    div.tooltip {   
        position: absolute;         
        text-align: center;
        min-width: 100;
        width: auto;    
        min-height:25;
        height: auto;                   
        padding: 2px;               
        font: 10px sans-serif;      
        background: rgba(0, 0, 0, 0.8);
        color: #fff;
        border: 0px;        
        border-radius: 8px;         
        pointer-events: none;           
    }

</style>
<body bgcolor="#A9A9A9">
<script type="text/javascript">
    graph = { "nodes":[{"proportions": [{"group": 1, "value": 1}, 
                                        {"group": 2, "value": 2}, 
                                        {"group": 3, "value": 3}],"Weight": "2","name":"abc1"},
                       {"proportions": [{"group": 1, "value": 2}, 
                                        {"group": 2, "value": 1}, 
                                        {"group": 3, "value": 5}],"Weight": "3","name":"abc2"},
                       {"proportions": [{"group": 1, "value": 7}, 
                                        {"group": 2, "value": 1}, 
                                        {"group": 3, "value": 3}],"Weight": "4","name":"abc3"},
                       {"proportions": [{"group": 1, "value": 5}, 
                                        {"group": 2, "value": 3}, 
                                        {"group": 3, "value": 4}],"Weight": "2","name":"abc4"},
                       {"proportions": [{"group": 1, "value": 2}, 
                                        {"group": 2, "value": 7}, 
                                        {"group": 3, "value": 3}],"Weight": "1","name":"abc5"}],
              "links":[{"source": 0, "target": 1, "width": 1},
                      {"source": 1, "target": 2, "width": 1},
                      {"source": 1, "target": 3, "width": 1},
                      {"source": 2, "target": 3, "width": 1},
                      {"source": 0, "target": 2, "width": 1},
                      {"source": 4, "target": 2, "width": 1}]
            }

    var width = 960,
        height = 500,
        radius = 25,
        color = d3.scale.category10(),
        rscale = d3.scale.linear().range([5,20]);

    var pie = d3.layout.pie()
        .sort(null)
        .value(function(d) { return d.value; });

    var arc = d3.svg.arc()
        .outerRadius(radius)
        .innerRadius(0);

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

    var force = d3.layout.force()
        .charge(-90)
        .gravity(0.09)
        .distance(100)
        .size([width, height]);

    force
        .nodes(graph.nodes)
        .links(graph.links)
        .start();

    rscale.domain(d3.extent(graph.nodes,function(d){ return d.Weight; }))

    var div = d3.select("body")
        .append("div")
        .attr("class", "tooltip")               
        .style("opacity", 0);

    var link = svg.selectAll(".link")
        .data(graph.links)
      .enter().append("line")
        .attr("class", "link");

    var node = svg.selectAll(".node")
        .data(graph.nodes)
        .enter().append("g")
        .attr("class", "node")
        .on("mouseover", function(d) {  
            var hoverArc=d3.svg.arc()
                    .outerRadius(50)
                    .innerRadius(0);

            d3.select(this).select("path").transition()
                .duration(250)
                .attr("d", hoverArc);

            div.transition()        
                .duration(200)      
                .style("opacity", .9);      
            div .html(d.name)   
                .style("left", (d3.event.pageX) + "px")     
                .style("top", (d3.event.pageY - 28) + "px");    
            })                  
        .on("mouseout", function(d) {       
            div.transition()        
                .duration(500)      
                .style("opacity", 0);   
            })
        .call(force.drag);

    node.each(function(d){
        arc = arc.outerRadius(rscale(d.Weight));

        d3.select(this)
        .selectAll("path")
            .data(function(d) {return pie(d.proportions); })
        .enter().append("path")
            .attr("d", arc)
            .style("fill", function(d,i) { return color(d.data.group); });
    });

    force.on("tick", function() {
      link.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; });

      node.attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });
    });
</script>
</body>
</html> 

我希望以上示例有助于了解我正在处理的内容。我非常感谢任何帮助我走向正确方向的帮助,我仍然是 D3 的新手。

而不是 select,它必须是 selectAll:

d3.select(this).selectAll("path").transition()
    .duration(250)
    .attr("d", hoverArc);

因为 this 是一个组元素,给定饼图中的所有路径都是该组的子元素。

这是您更新后的代码:

graph = {
  "nodes": [{
    "proportions": [{
      "group": 1,
      "value": 1
    }, {
      "group": 2,
      "value": 2
    }, {
      "group": 3,
      "value": 3
    }],
    "Weight": "2",
    "name": "abc1"
  }, {
    "proportions": [{
      "group": 1,
      "value": 2
    }, {
      "group": 2,
      "value": 1
    }, {
      "group": 3,
      "value": 5
    }],
    "Weight": "3",
    "name": "abc2"
  }, {
    "proportions": [{
      "group": 1,
      "value": 7
    }, {
      "group": 2,
      "value": 1
    }, {
      "group": 3,
      "value": 3
    }],
    "Weight": "4",
    "name": "abc3"
  }, {
    "proportions": [{
      "group": 1,
      "value": 5
    }, {
      "group": 2,
      "value": 3
    }, {
      "group": 3,
      "value": 4
    }],
    "Weight": "2",
    "name": "abc4"
  }, {
    "proportions": [{
      "group": 1,
      "value": 2
    }, {
      "group": 2,
      "value": 7
    }, {
      "group": 3,
      "value": 3
    }],
    "Weight": "1",
    "name": "abc5"
  }],
  "links": [{
    "source": 0,
    "target": 1,
    "width": 1
  }, {
    "source": 1,
    "target": 2,
    "width": 1
  }, {
    "source": 1,
    "target": 3,
    "width": 1
  }, {
    "source": 2,
    "target": 3,
    "width": 1
  }, {
    "source": 0,
    "target": 2,
    "width": 1
  }, {
    "source": 4,
    "target": 2,
    "width": 1
  }]
}

var width = 600,
  height = 400,
  radius = 25,
  color = d3.scale.category10(),
  rscale = d3.scale.linear().range([5, 20]);

var pie = d3.layout.pie()
  .sort(null)
  .value(function(d) {
    return d.value;
  });

var arc = d3.svg.arc()
  .outerRadius(radius)
  .innerRadius(0);

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

var force = d3.layout.force()
  .charge(-90)
  .gravity(0.09)
  .distance(100)
  .size([width, height]);

force
  .nodes(graph.nodes)
  .links(graph.links)
  .start();

rscale.domain(d3.extent(graph.nodes, function(d) {
  return d.Weight;
}))

var div = d3.select("body")
  .append("div")
  .attr("class", "tooltip")
  .style("opacity", 0);

var link = svg.selectAll(".link")
  .data(graph.links)
  .enter().append("line")
  .attr("class", "link");

var node = svg.selectAll(".node")
  .data(graph.nodes)
  .enter().append("g")
  .attr("class", "node")
  .on("mouseover", function(d) {
    var hoverArc = d3.svg.arc()
      .outerRadius(50)
      .innerRadius(0);

    d3.select(this).selectAll("path").transition()
      .duration(250)
      .attr("d", hoverArc);

    div.transition()
      .duration(200)
      .style("opacity", .9);
    div.html(d.name)
      .style("left", (d3.event.pageX) + "px")
      .style("top", (d3.event.pageY - 28) + "px");
  })
  .on("mouseout", function(d) {

    var hoverOutarc = d3.svg.arc()
      .outerRadius(rscale(d.Weight))
      .innerRadius(0);

    d3.select(this).selectAll("path").transition()
      .duration(250)
      .attr("d", hoverOutarc);

    div.transition()
      .duration(500)
      .style("opacity", 0);
  })
  .call(force.drag);

node.each(function(d) {
  arc = arc.outerRadius(rscale(d.Weight));

  d3.select(this)
    .selectAll("path")
    .data(function(d) {
      return pie(d.proportions);
    })
    .enter().append("path")
    .attr("d", arc)
    .style("fill", function(d, i) {
      return color(d.data.group);
    });
});

force.on("tick", function() {
  link.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;
    });

  node.attr('transform', function(d) {
    return 'translate(' + d.x + ',' + d.y + ')';
  });
});
.link {
  fill: none;
  stroke: #000000;
  stroke-width: 1.5px;
  stroke-opacity: 0.8;
}

div.tooltip {
  position: absolute;
  text-align: center;
  min-width: 100;
  width: auto;
  min-height: 25;
  height: auto;
  padding: 2px;
  font: 10px sans-serif;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>