简体   繁体   English

如何使画笔的范围与父元素的大小相匹配?

[英]How to make the extent of a brush match the size of the parent element?

I am trying to combine d3-brush with d3-zoom in a force-directed graph. 我试图在强制导向图中将d3-brushd3-zoom结合起来。 Although I managed to combine the modules, it seems that I am not able to use the brush on the whole g element which contains the nodes.Thus, I am not able to select all the nodes in the graph. 虽然我设法组合模块,但似乎我无法在包含节点的整个g元素上使用画笔。因此,我无法选择图中的所有节点。

I have already had a look at some blocks, eg D3 Selectable Force-Directed Graph , https://bl.ocks.org/mbostock/4566102 , D3v4 Selectable, Draggable, Zoomable Force Directed Graph which try to combine the two modules [ 1 , 3 ] or use the brush event in a force-directed graph [ 2 ]. 我已经看过一些块,例如D3 Selectable Force-Directed Graphhttps//bl.ocks.org/mbostock/4566102,D3v4 Selectable,Draggable,Zoomable Force Directed Graph ,试图将这两个模块结合起来[ 13 ]或在力导向图[ 2 ]中使用画笔事件。 Moreover, I have also read the d3-brush API . 此外,我还阅读了d3-brush API However, I can not achieve the desired behavior. 但是,我无法实现理想的行为。

The user can select a region of points in the graph by holding the ctrl key and pressing the left mouse button ( ctrl key + lmb ). 用户可以通过按住ctrl键并按下鼠标左键ctrl键+ lmb )来选择图表中的点区域。

Theoretically, the user should be able to select all the nodes within the g element which contains the nodes. 从理论上讲,用户应该能够选择包含节点的g元素中的所有节点。 In the next figure you can see the maximum brush extent. 在下图中,您可以看到最大画笔范围。 Nodes outside the brush extent can not be selected. 无法选择画笔范围之外的节点。

最大画笔范围

In the figure below, you can see the DOM hierarchy of the HTML document. 在下图中,您可以看到HTML文档的DOM层次结构。

DOM层次结构

I am suspecting that the extent of the brush is not defined correctly, which causes this issue.If I apply the brush to the svgForce element and the nodes are also drawn directly on the svgForce element, then the brush extent matches the dimensions of the svg . 我怀疑刷子的范围没有正确定义,这导致了这个问题。如果我将画笔应用于svgForce元素并且节点也直接在svgForce元素上绘制,那么画笔范围与svg的尺寸相匹配。 However, in that case, the zoom does not work properly, because it is not bound to a g element. 但是,在这种情况下,缩放不能正常工作,因为它没有绑定到g元素。

svgForce.append("g")
.attr("class", "brush")
.call(d3.brush().on("brush", brushed));

Do you have any ideas what might be going wrong? 你有什么想法可能会出错吗?

 var width_network = 700, height_network = 700, gForce, gMain, zoom, brush, CIRCLE_RADIUS = 10, link, node, nodeLabel, gBrush; var network = {"nodes":[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"},{"name":"F"},{"name":"G"},{"name":"H"},{"name":"I"},{"name":"J"},{"name":"K"},{"name":"L"},{"name":"M"},{"name":"N"},{"name":"O"},{"name":"P"},{"name":"Q"},{"name":"R"},{"name":"S"},{"name":"T"},{"name":"U"},{"name":"V"},{"name":"W"},{"name":"X"},{"name":"Y"},{"name":"Z"},{"name":"AA"},{"name":"BB"},{"name":"CC"},{"name":"DD"},{"name":"EE"},{"name":"FF"},{"name":"GG"},{"name":"HH"},{"name":"II"},{"name":"JJ"},{"name":"KK"},{"name":"LL"},{"name":"MM"},{"name":"NN"},{"name":"OO"},{"name":"PP"},{"name":"QQ"},{"name":"RR"},{"name":"SS"},{"name":"TT"},{"name":"UU"},{"name":"VV"},{"name":"WW"},{"name":"XX"},{"name":"YY"},{"name":"ZZ"},{"name":"AAA"},{"name":"BBB"},{"name":"CCC"},{"name":"DDD"},{"name":"EEE"},{"name":"FFF"},{"name":"GGG"},{"name":"HHH"},{"name":"III"},{"name":"JJJ"},{"name":"KKK"},{"name":"LLL"},{"name":"MMM"},{"name":"NNN"},{"name":"OOO"},{"name":"PPP"},{"name":"QQQ"},{"name":"RRR"},{"name":"SSS"},{"name":"TTT"},{"name":"UUU"},{"name":"VVV"},{"name":"WWW"},{"name":"XXX"},{"name":"YYY"},{"name":"ZZZ"},{"name":"AAAA"},{"name":"BBBB"},{"name":"CCCC"},{"name":"DDDD"},{"name":"EEEE"},{"name":"FFFF"},{"name":"GGGG"},{"name":"HHHH"},{"name":"IIII"},{"name":"JJJJ"},{"name":"KKKK"},{"name":"LLLL"},{"name":"MMMM"},{"name":"NNNN"},{"name":"OOOO"},{"name":"PPPP"},{"name":"QQQQ"},{"name":"RRRR"},{"name":"SSSS"},{"name":"TTTT"},{"name":"UUUU"},{"name":"VVVV"},{"name":"WWWW"},{"name":"XXXX"},{"name":"YYYY"},{"name":"ZZZZ"},{"name":"A1"},{"name":"B2"},{"name":"C3"},{"name":"D4"},{"name":"E5"},{"name":"F6"},{"name":"G7"},{"name":"H8"},{"name":"I9"},{"name":"J10"},{"name":"K11"},{"name":"L12"},{"name":"M13"},{"name":"N14"},{"name":"O15"},{"name":"P16"},{"name":"Q17"},{"name":"R18"},{"name":"S19"},{"name":"T20"},{"name":"U21"},{"name":"V22"},{"name":"W23"},{"name":"X24"},{"name":"Y25"},{"name":"Z26"},{"name":"A27"},{"name":"B28"},{"name":"C29"},{"name":"D30"},{"name":"E31"},{"name":"F32"},{"name":"G33"},{"name":"H34"},{"name":"I35"},{"name":"J36"},{"name":"K37"},{"name":"L38"},{"name":"M39"},{"name":"N40"},{"name":"O41"},{"name":"P42"},{"name":"Q43"},{"name":"R44"},{"name":"S45"},{"name":"T46"},{"name":"U47"},{"name":"V48"},{"name":"W49"},{"name":"X50"},{"name":"Y51"},{"name":"Z52"},{"name":"A53"},{"name":"B54"},{"name":"C55"},{"name":"D56"},{"name":"E57"},{"name":"F58"},{"name":"G59"},{"name":"H60"},{"name":"I61"},{"name":"J62"},{"name":"K63"},{"name":"L64"},{"name":"M65"},{"name":"N66"},{"name":"O67"},{"name":"P68"},{"name":"Q69"},{"name":"R70"},{"name":"S71"},{"name":"T72"},{"name":"U73"},{"name":"V74"},{"name":"W75"},{"name":"X76"},{"name":"Y77"},{"name":"Z78"},{"name":"A79"},{"name":"B80"},{"name":"C81"},{"name":"D82"},{"name":"E83"},{"name":"F84"},{"name":"G85"},{"name":"H86"},{"name":"I87"},{"name":"J88"},{"name":"K89"},{"name":"L90"},{"name":"M91"},{"name":"N92"},{"name":"O93"},{"name":"P94"},{"name":"Q95"},{"name":"R96"},{"name":"S97"},{"name":"T98"},{"name":"U99"},{"name":"V100"},{"name":"W101"},{"name":"X102"},{"name":"Y103"},{"name":"Z104"},{"name":"A105"},{"name":"B106"},{"name":"C107"},{"name":"D108"},{"name":"E109"},{"name":"F110"},{"name":"G112"},{"name":"H113"},{"name":"I114"},{"name":"J115"},{"name":"K116"},{"name":"L117"},{"name":"M118"},{"name":"N119"},{"name":"O120"},{"name":"P121"},{"name":"Q123"},{"name":"R124"},{"name":"S125"},{"name":"T126"},{"name":"U127"},{"name":"V128"},{"name":"W129"},{"name":"X130"},{"name":"Y131"},{"name":"Z134"},{"name":"A135"},{"name":"B136"},{"name":"C137"},{"name":"D138"},{"name":"E139"},{"name":"F140"},{"name":"G141"}],"links":[{"source":0,"target":1,"value":375},{"source":0,"target":2,"value":27},{"source":0,"target":3,"value":15},{"source":0,"target":4,"value":8},{"source":0,"target":5,"value":6},{"source":0,"target":6,"value":4},{"source":0,"target":7,"value":3},{"source":0,"target":8,"value":3},{"source":0,"target":9,"value":2},{"source":0,"target":10,"value":2},{"source":0,"target":11,"value":2},{"source":0,"target":12,"value":2},{"source":0,"target":13,"value":2},{"source":0,"target":14,"value":2},{"source":0,"target":15,"value":1},{"source":0,"target":16,"value":1},{"source":0,"target":17,"value":1},{"source":0,"target":18,"value":1},{"source":0,"target":19,"value":1},{"source":0,"target":20,"value":1},{"source":0,"target":21,"value":1},{"source":0,"target":22,"value":1},{"source":0,"target":23,"value":87},{"source":0,"target":24,"value":24},{"source":0,"target":25,"value":20},{"source":0,"target":26,"value":20},{"source":0,"target":27,"value":19},{"source":0,"target":28,"value":17},{"source":0,"target":29,"value":12},{"source":0,"target":30,"value":6},{"source":0,"target":31,"value":5},{"source":0,"target":32,"value":5},{"source":0,"target":33,"value":4},{"source":0,"target":34,"value":4},{"source":0,"target":35,"value":3},{"source":0,"target":36,"value":3},{"source":0,"target":37,"value":3},{"source":0,"target":38,"value":3},{"source":0,"target":39,"value":3},{"source":0,"target":40,"value":3},{"source":0,"target":41,"value":2},{"source":0,"target":42,"value":2},{"source":0,"target":43,"value":2},{"source":0,"target":44,"value":2},{"source":0,"target":45,"value":1},{"source":0,"target":46,"value":1},{"source":0,"target":47,"value":1},{"source":0,"target":48,"value":1},{"source":0,"target":49,"value":1},{"source":0,"target":50,"value":1},{"source":0,"target":51,"value":1},{"source":0,"target":52,"value":1},{"source":0,"target":53,"value":1},{"source":0,"target":54,"value":1},{"source":0,"target":55,"value":34},{"source":0,"target":56,"value":13},{"source":0,"target":57,"value":8},{"source":0,"target":58,"value":8},{"source":0,"target":59,"value":5},{"source":0,"target":60,"value":5},{"source":0,"target":61,"value":4},{"source":0,"target":62,"value":4},{"source":0,"target":63,"value":3},{"source":0,"target":64,"value":3},{"source":0,"target":65,"value":3},{"source":0,"target":66,"value":2},{"source":0,"target":67,"value":2},{"source":0,"target":68,"value":2},{"source":0,"target":69,"value":2},{"source":0,"target":70,"value":2},{"source":0,"target":71,"value":2},{"source":0,"target":72,"value":2},{"source":0,"target":73,"value":1},{"source":0,"target":74,"value":1},{"source":0,"target":75,"value":1},{"source":0,"target":76,"value":1},{"source":0,"target":77,"value":1},{"source":0,"target":78,"value":1},{"source":0,"target":79,"value":1},{"source":0,"target":80,"value":1},{"source":0,"target":81,"value":1},{"source":0,"target":82,"value":1},{"source":0,"target":83,"value":1},{"source":0,"target":84,"value":1},{"source":0,"target":85,"value":1},{"source":0,"target":86,"value":1},{"source":0,"target":87,"value":1},{"source":0,"target":88,"value":1},{"source":0,"target":89,"value":1},{"source":0,"target":90,"value":1},{"source":0,"target":91,"value":1},{"source":0,"target":92,"value":1},{"source":0,"target":93,"value":1},{"source":0,"target":94,"value":1},{"source":0,"target":95,"value":11},{"source":0,"target":96,"value":7},{"source":0,"target":97,"value":6},{"source":0,"target":98,"value":3},{"source":0,"target":99,"value":3},{"source":0,"target":100,"value":2},{"source":0,"target":101,"value":1},{"source":0,"target":102,"value":1},{"source":0,"target":103,"value":1},{"source":0,"target":104,"value":1},{"source":0,"target":105,"value":1},{"source":0,"target":106,"value":1},{"source":0,"target":107,"value":1},{"source":0,"target":108,"value":1},{"source":0,"target":109,"value":1},{"source":0,"target":110,"value":1},{"source":0,"target":111,"value":1},{"source":0,"target":112,"value":1},{"source":0,"target":113,"value":1},{"source":0,"target":114,"value":1},{"source":0,"target":115,"value":1},{"source":0,"target":116,"value":1},{"source":0,"target":117,"value":9},{"source":0,"target":118,"value":7},{"source":0,"target":119,"value":1},{"source":0,"target":120,"value":1},{"source":0,"target":121,"value":1},{"source":0,"target":122,"value":1},{"source":0,"target":123,"value":8},{"source":0,"target":124,"value":4},{"source":0,"target":125,"value":2},{"source":0,"target":126,"value":2},{"source":0,"target":127,"value":2},{"source":0,"target":128,"value":1},{"source":0,"target":129,"value":4},{"source":0,"target":130,"value":4},{"source":0,"target":131,"value":3},{"source":0,"target":132,"value":2},{"source":0,"target":133,"value":2},{"source":0,"target":134,"value":1},{"source":0,"target":135,"value":1},{"source":0,"target":136,"value":1},{"source":0,"target":137,"value":8},{"source":0,"target":138,"value":3},{"source":0,"target":139,"value":2},{"source":0,"target":140,"value":1},{"source":0,"target":141,"value":1},{"source":0,"target":142,"value":4},{"source":0,"target":143,"value":3},{"source":0,"target":144,"value":2},{"source":0,"target":145,"value":2},{"source":0,"target":146,"value":2},{"source":0,"target":147,"value":1},{"source":0,"target":148,"value":6},{"source":0,"target":149,"value":3},{"source":0,"target":150,"value":1},{"source":0,"target":151,"value":1},{"source":0,"target":152,"value":1},{"source":0,"target":153,"value":1},{"source":0,"target":154,"value":1},{"source":0,"target":155,"value":6},{"source":0,"target":156,"value":2},{"source":0,"target":157,"value":2},{"source":0,"target":158,"value":1},{"source":0,"target":159,"value":1},{"source":0,"target":160,"value":1},{"source":0,"target":161,"value":2},{"source":0,"target":162,"value":1},{"source":0,"target":163,"value":1},{"source":0,"target":164,"value":1},{"source":0,"target":165,"value":1},{"source":0,"target":166,"value":1},{"source":0,"target":167,"value":1},{"source":0,"target":168,"value":1},{"source":0,"target":169,"value":5},{"source":0,"target":170,"value":2},{"source":0,"target":171,"value":1},{"source":0,"target":172,"value":2},{"source":0,"target":173,"value":2},{"source":0,"target":174,"value":1},{"source":0,"target":175,"value":1},{"source":0,"target":176,"value":1},{"source":0,"target":177,"value":1},{"source":0,"target":178,"value":2},{"source":0,"target":179,"value":2},{"source":0,"target":180,"value":1},{"source":0,"target":181,"value":1},{"source":0,"target":182,"value":1},{"source":0,"target":183,"value":1},{"source":0,"target":184,"value":1},{"source":0,"target":185,"value":1},{"source":0,"target":186,"value":1},{"source":0,"target":187,"value":1},{"source":0,"target":188,"value":1},{"source":0,"target":189,"value":1},{"source":0,"target":190,"value":2},{"source":0,"target":191,"value":1},{"source":0,"target":192,"value":1},{"source":0,"target":194},{"source":194,"target":193},{"source":0,"target":195},{"source":195,"target":193},{"source":0,"target":196},{"source":196,"target":193},{"source":0,"target":197,"value":27},{"source":0,"target":199},{"source":199,"target":198},{"source":0,"target":201},{"source":201,"target":200},{"source":0,"target":194},{"source":194,"target":202},{"source":0,"target":195},{"source":195,"target":202},{"source":0,"target":196},{"source":196,"target":202},{"source":0,"target":204},{"source":204,"target":203},{"source":0,"target":206},{"source":206,"target":205},{"source":0,"target":208},{"source":208,"target":207},{"source":0,"target":201},{"source":201,"target":209},{"source":0,"target":194},{"source":194,"target":210},{"source":0,"target":199},{"source":199,"target":211},{"source":0,"target":213},{"source":213,"target":212},{"source":0,"target":214,"value":2},{"source":0,"target":216},{"source":216,"target":215},{"source":0,"target":218},{"source":218,"target":217},{"source":0,"target":218},{"source":218,"target":219},{"source":0,"target":204},{"source":204,"target":220},{"source":0,"target":194},{"source":194,"target":221},{"source":0,"target":194},{"source":194,"target":222},{"source":0,"target":208},{"source":208,"target":223},{"source":0,"target":225},{"source":225,"target":224},{"source":0,"target":227},{"source":227,"target":226},{"source":0,"target":229},{"source":229,"target":228},{"source":0,"target":230,"value":1},{"source":0,"target":231,"value":1},{"source":0,"target":232,"value":1},{"source":0,"target":233,"value":1},{"source":0,"target":234,"value":1},{"source":0,"target":235,"value":1},{"source":0,"target":236,"value":1},{"source":0,"target":237,"value":1},{"source":0,"target":194},{"source":194,"target":238},{"source":0,"target":194},{"source":194,"target":239},{"source":0,"target":194},{"source":194,"target":240}]}; zoom = d3.zoom() .scaleExtent([0.1, 10]) .on("zoom", zoomed); var container = d3.select("#network_group"); var svgForce = container .append("svg") .attr("id", "network_svg") .attr("width", width_network) .attr("height", height_network); gMain = svgForce.append("g") .attr("class","gMain"); var rect = gMain.append('rect') .attr('width', width_network) .attr('height', height_network) .style('fill', 'white'); gForce = gMain.append("g"); gMain.call(zoom) .on("dblclick.zoom", null); var simulation = d3.forceSimulation(network.nodes).force("charge", d3.forceManyBody().strength(-200)) .force("collision", d3.forceCollide().radius(CIRCLE_RADIUS*2)) .force("x", d3.forceX(width_network/2).strength(0.015)) .force("y", d3.forceY(height_network/2).strength(0.02)) .force("center", d3.forceCenter(width_network/2, height_network/2)) .force("link", d3.forceLink(network.links).distance(70)); var gBrushHolder = gForce.append('g'); var gBrush = null; link = gForce.append("g") .attr("class", "force_links") .selectAll("line") .data(network.links) .enter() .append("line") .attr("stroke-width", 2); // define nodes node = gForce.append("g") .attr("class", "force_nodes") .selectAll("circle") .data(network.nodes) .enter() .append("circle") .attr("r", CIRCLE_RADIUS) .on("mouseover", function (d) { d3.select(this).append("title") .text(function (d) { return d.name; }); }) .on("mouseout", function (d) { d3.select(this).select("title").remove(); }) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); // define node labels nodeLabel = gForce.append("g") .attr("class", "label_nodes") .selectAll("text") .data(network.nodes) .enter() .append("text") .text(function (d) { return d.name; }) .style("text-anchor", "start"); simulation.on("tick", ticked); function ticked() { node.attr("cx", function (d) { return dx; }) .attr("cy", function (d) { return dy; }); 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; }); nodeLabel.attr("x", function (d) { return dx; }) .attr("y", function (d) { return dy - 13; }); } var brushMode = false; var brushing = false; var brush = d3.brush() .extent([[0,0],[width_network,height_network]]) .on("start", brush_start) .on("brush", brushed) .on("end", brush_end); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); if (d.fixed) { d.fixed = false; d.fx = null; d.fy = null; d3.select(this) .style("fill","lightsteelblue") .style("stroke", "#fff") .style("stroke-width", "1.5px"); } else { d3.select(this) .style("stroke", "lightsteelblue") .style("fill", "white") .style("stroke-width", 9); d.fixed = true; } } function zoomed() { var transform = d3.event.transform; gForce.attr("transform", transform); } if (network.nodes.length > 80) { zoom.translateTo(svgForce, (width_network - width_network * 0.4) / 2, (height_network - height_network * 0.4) / 2); zoom.scaleTo(svgForce, 0.4); } function brush_start(){ brushing = true; node.each(function(d) { d.previouslySelected = ctrlKey && d.selected; }); } rect.on('click', () => { node.each(function(d) { d.selected = false; d.previouslySelected = false; }); node.classed("selected", false); }); function brushed() { if (!d3.event.sourceEvent) return; if (!d3.event.selection) return; var extent = d3.event.selection; node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= dx && dx < extent[1][0] && extent[0][1] <= dy && dy < extent[1][1]); }); } function brush_end(){ if (!d3.event.sourceEvent) return; if (!d3.event.selection) return; if (!gBrush) return; gBrush.call(brush.move, null); if (!brushMode) { // the shift key has been release before we ended our brushing gBrush.remove(); gBrush = null; } brushing = false; } d3.select('body').on('keydown', keydown); d3.select('body').on('keyup', keyup); var ctrlKey; function keydown() { ctrlKey = d3.event.ctrlKey; if (ctrlKey) { if (gBrush) return; brushMode = true; if (!gBrush) { gBrush = gBrushHolder.append("g").attr("class", "brush"); gBrush.call(brush); } } } function keyup() { ctrlKey = false; brushMode = false; if (!gBrush) return; if (!brushing) { gBrush.remove(); gBrush = null; } } 
 .force_nodes { fill: lightsteelblue; } .force_links { stroke: gray; } .force_nodes .selected { stroke: red; } .label_nodes text { cursor: pointer; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <div id="network_group"></div> 

You can find a working version of the code also in this JSFiddle . 您可以在此JSFiddle中找到该代码的工作版本。

The problem with your code right now is that you're appending the brush group to a group that you later translate in the zoom function: 您的代码现在的问题是您将刷组附加到稍后在缩放功能中转换的组:

var gBrushHolder = gForce.append('g');

Instead of that, append the brush group to the main group: 而不是,将画笔组附加到主组:

var gBrushHolder = gMain.append('g');

Then, use a variable to track the zoom's translate and scale... 然后,使用变量来跟踪缩放的平移和缩放...

//declare it:
let currentZoom;

//update its value in the zoom function:
currentZoom = d3.event.transform;

Finally, use that value to get the SVG position of the nodes: 最后,使用该值获取节点的SVG位置:

node.classed("selected", function(d) {
    return d.selected = d.previouslySelected ^
        (extent[0][0] <= (d.x * currentZoom.k + currentZoom.x) && (d.x * currentZoom.k + currentZoom.x) < extent[1][0] && extent[0][1] <= (d.y * currentZoom.k + currentZoom.y) && (d.y * currentZoom.k + currentZoom.y) < extent[1][1]);
});

Here is the updated JSFiddle: https://jsfiddle.net/uxqf7tp2/ 这是更新的JSFiddle: https ://jsfiddle.net/uxqf7tp2/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM