簡體   English   中英

如何使畫筆的范圍與父元素的大小相匹配?

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

我試圖在強制導向圖中將d3-brushd3-zoom結合起來。 雖然我設法組合模塊,但似乎我無法在包含節點的整個g元素上使用畫筆。因此,我無法選擇圖中的所有節點。

我已經看過一些塊,例如D3 Selectable Force-Directed Graphhttps//bl.ocks.org/mbostock/4566102,D3v4 Selectable,Draggable,Zoomable Force Directed Graph ,試圖將這兩個模塊結合起來[ 13 ]或在力導向圖[ 2 ]中使用畫筆事件。 此外,我還閱讀了d3-brush API 但是,我無法實現理想的行為。

用戶可以通過按住ctrl鍵並按下鼠標左鍵ctrl鍵+ lmb )來選擇圖表中的點區域。

從理論上講,用戶應該能夠選擇包含節點的g元素中的所有節點。 在下圖中,您可以看到最大畫筆范圍。 無法選擇畫筆范圍之外的節點。

最大畫筆范圍

在下圖中,您可以看到HTML文檔的DOM層次結構。

DOM層次結構

我懷疑刷子的范圍沒有正確定義,這導致了這個問題。如果我將畫筆應用於svgForce元素並且節點也直接在svgForce元素上繪制,那么畫筆范圍與svg的尺寸相匹配。 但是,在這種情況下,縮放不能正常工作,因為它沒有綁定到g元素。

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

你有什么想法可能會出錯嗎?

 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> 

您可以在此JSFiddle中找到該代碼的工作版本。

您的代碼現在的問題是您將刷組附加到稍后在縮放功能中轉換的組:

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

而不是,將畫筆組附加到主組:

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

然后,使用變量來跟蹤縮放的平移和縮放...

//declare it:
let currentZoom;

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

最后,使用該值獲取節點的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]);
});

這是更新的JSFiddle: https ://jsfiddle.net/uxqf7tp2/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM