如何使用voronoi多边形修改d3力布局以触发分组元素上的事件? [英] How to modify a d3 force layout with voronoi polygons to trigger events on grouped elements?
问题描述
目标是将d3力模拟,g元素和voronoi多边形相结合,以使节点上的触发事件更容易,例如拖动,鼠标悬停,工具提示等,并可以通过 d3圆拖IV示例的示例.
The goal is to combine d3 force simulation, g elements, and voronoi polygons to make trigger events on nodes easier, such as dragging, mouseovers, tooltips and so on with a graph that can be dynamically modified. This follows the d3 Circle Dragging IV example.
在以下代码中,将clip path属性添加到g元素和clippath元素时:
In the following code, when adding the clip path attribute to the g element and clippath elements:
- 为什么拖动不触发单元格?
- 为什么节点变得模糊并且 路径失去了边缘的样式?
- 如何解决此问题以拖动节点并在其上触发事件,例如鼠标悬停?
- Why does dragging not trigger on the cells?
- Why do the nodes become obscured and the paths lose their styles on edges?
- How can this be fixed to drag the nodes and trigger events on them like mouseovers?
var data = [
{
"index" : 0,
"vx" : 0,
"vy" : 0,
"x" : 842,
"y" : 106
},
{
"index" : 1,
"vx" : 0,
"vy" : 0,
"x" : 839,
"y" : 56
},
{
"index" : 2,
"vx" : 0,
"vy" : 0,
"x" : 771,
"y" : 72
}
]
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var simulation = d3.forceSimulation(data)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
var nodes = svg.append("g").attr("class", "nodes"),
node = nodes.selectAll("g"),
paths = svg.append("g").attr("class", "paths"),
path = paths.selectAll("path");
var voronoi = d3.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.extent([[0, 0], [width, height]]);
var update = function() {
node = nodes.selectAll("g").data(data);
var nodeEnter = node.enter()
.append("g")
.attr("class", "node")
.attr("clip-path", function(d, i) { return "url(#clip-" + i + ")"; });
nodeEnter.append("circle");
nodeEnter.append("text")
.text(function(d, i) { return i; });
node.merge(nodeEnter);
path = paths.selectAll(".path")
.data(data)
.enter().append("clipPath")
.attr("id", function(d, i) { return "clip-" + i; })
.append("path")
.attr("class", "path");
simulation.nodes(data);
simulation.restart();
}();
function ticked() {
var node = nodes.selectAll("g");
var diagram = voronoi(node.data()).polygons();
paths.selectAll("path")
.data(diagram)
.enter()
.append("clipPath")
.attr("id", function(d, i) { return "clip-" + i; })
.append("path")
.attr("class", "path");
paths.selectAll("path")
.attr("d", function(d) { return d == null ? null : "M" + d.join("L") + "Z"; });
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
svg {
border: 1px solid #888888;
}
circle {
r: 3;
cursor: move;
fill: black;
}
.node {
pointer-events: all;
}
path {
fill: none;
stroke: #999;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.1/d3.js"></script>
<svg width="400" height="400"></svg>
(单独的问题,但是将路径嵌套在圆拖动IV"元素中的g元素中会导致路径不希望地定位到图形的侧面.)
(Separate question, but nesting the paths in the g elements as in the Circle Dragging IV element causes undesired positioning of the paths off to the side of the graph.)
在相关问题,使用多边形而不是路径和剪切路径,我可以将其拖到工作中,但是我试图使用剪切路径版本作为比较,但不确定有什么区别,除了剪切路径似乎更可取由Mike Bostock(d3创作者)完成.
In a related question, using polygons instead of paths and clippaths, I can get the dragging to work, but am trying to use the clippath version as a comparison and not sure what are the differences, other than clippath seems to be preferred by Mike Bostock (d3 creator).
推荐答案
Block 版本.
- 为什么拖动不会在单元格上触发?
- 因为单元格属性具有fill:none,那么它必须具有指针事件:all.
- Why does dragging not trigger on the cells?
- Because if the cell attribute has fill:none, then it must have pointer-events:all.
- 因为剪辑路径的目标是g元素位置而不是圆形位置.
- 使用路径attr指针事件:全部
path { pointer-events: all; }
- 在拖动或打勾事件中选择所需的子元素,例如圆圈或文本,以定位
parent.select(child).attr('d' function(d) { ..do stuff.. });
- 使用节点ID作为参考来简化数据阵列的更新或删除
node.data(data, function(d) { return d.id; })
- use path attr pointer-events: all,
path { pointer-events: all; }
- select the desired child element such as circle, or text, in the drag or tick event for positioning
parent.select(child).attr('d' function(d) { ..do stuff.. });
- use node id's for references to simplify data array updates or deletions
node.data(data, function(d) { return d.id; })
感谢安德鲁·里德(Andrew Reid)的帮助.
Thanks Andrew Reid for your help.
var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"), color = d3.scaleOrdinal(d3.schemeCategory10); var a = {id: "a"}, b = {id: "b"}, c = {id: "c"}, data = [a, b, c], links = []; var simulation = d3.forceSimulation(data) .force("charge", d3.forceManyBody().strength(-10)) .force("link", d3.forceLink(links).distance(200)) .force("center", d3.forceCenter(width / 2, height / 2)) .alphaTarget(1) .on("tick", ticked); var link = svg.append("g").attr("class", "links").selectAll(".link"), node = svg.append("g").attr("class", "nodes").selectAll(".node"); var voronoi = d3.voronoi() .x(function(d) { return d.x; }) .y(function(d) { return d.y; }) .extent([[-1, 1], [width + 1, height + 1]]); update(); d3.timeout(function() { links.push({source: a, target: b}); // Add a-b. links.push({source: b, target: c}); // Add b-c. links.push({source: c, target: a}); // Add c-a. update(); }, 1000); d3.interval(function() { data.pop(); // Remove c. links.pop(); // Remove c-a. links.pop(); // Remove b-c. update(); }, 5000, d3.now()); d3.interval(function() { data.push(c); // Re-add c. links.push({source: b, target: c}); // Re-add b-c. links.push({source: c, target: a}); // Re-add c-a. update(); }, 5000, d3.now() + 1000); function update() { node = node.data(data, function(d) { return d.id; }); node.exit().remove(); var nodeEnter = node.enter().append("g") .attr("class", "node") .on("mouseover", mouseover) .on("mouseout", mouseout); nodeEnter.append("circle").attr("fill", function(d) { return color(d.id); }).attr("r", 8); nodeEnter.append("text") .attr("dx", 12) .attr("dy", ".35em") .text(function(d) { return d.id; }); nodeEnter.append("path").attr("class", "path"); nodeEnter.call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); node = node.merge(nodeEnter); // Apply the general update pattern to the links. link = link.data(links, function(d) { return d.source.id + "-" + d.target.id; }); link.exit().remove(); link = link.enter().append("line").merge(link); // Update and restart the simulation. simulation.nodes(data); simulation.force("link").links(links); simulation.alpha(1).restart(); } function mouseover(d) { d3.select(this).raise().classed("active", true); } function mouseout(d) { d3.select(this).raise().classed("active", false); } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d3.select(this).select("circle").attr("cx", d.fx = d3.event.x).attr("cy", d.fy = d3.event.y); } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } function ticked() { node.select("circle") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); node.select("path") .data(voronoi.polygons(data)) .attr("d", function(d) { return d == null ? null : "M" + d.join("L") + "Z"; }); node.select("text") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" }); 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; }); }
path { pointer-events: all; fill: none; stroke: #666; stroke-opacity: 0.2; } .active path { fill: #111; opacity: 0.05; } .active text { visibility: visible; } .active circle { stroke: #000; stroke-width: 1.5px; } svg { border: 1px solid #888; } .links { stroke: #000; stroke-width: 1.5; } .nodes { stroke-width: 1.5; } text { pointer-events: none; font: 1.8em sans-serif; visibility: hidden; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script> <svg width="400" height="400"></svg>
这篇关于如何使用voronoi多边形修改d3力布局以触发分组元素上的事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!