如何使用 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>
我正在 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>