#javascript #d3.js
#язык JavaScript #d3.js
Вопрос:
Я работаю над небольшой D3.js график и хотите скрыть подключенные узлы меньшего размера, если был нажат родительский узел большего размера. До сих пор я пробовал несколько подходов, и моя лучшая попытка-сначала отфильтровать ссылки, чтобы впоследствии получить исходные узлы.
К сожалению , я получаю newLinks.map is not a function error
, либо я полностью не понимаю функцию карты, либо просто пропускаю окончательный мир.
function onClick(event, d) { const newLinks = link.filter(link =gt; link.target.id === d.id); console.log(newLinks) const newNodes = newLinks.map(link =gt; data.nodes.find(newNode =gt; newNode.id === link.source.id)) console.log(newNodes) }
Вопрос: Как я могу достичь того, к чему стремлюсь? Я хочу скрыть меньшие присоединенные узлы, если был нажат родительский узел.
var svg = d3.select("body").append("svg") .attr("width", window.innerWidth) .attr("height", window.innerHeight) var data = { "nodes": [{ "id": "A", "type": "parent" }, { "id": "B", "type": "parent" }, { "id": "C", "type": "child" }, { "id": "D", "type": "child" }, { "id": "E", "type": "child" }, ], "links": [{ "source": "A", "target": "B", "distance": 125 }, { "source": "C", "target": "A", "distance": 20 }, { "source": "D", "target": "A", "distance": 20 }, { "source": "E", "target": "B", "distance": 20 }, ] } var force = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id }).distance(function(d) { return d.distance })) .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2)) .force("charge", d3.forceManyBody().strength(-1000)) .force("collision", d3.forceCollide().radius(setSize)) var linksContainer = svg.append("g").attr("class", "linksContainer") var nodesContainer = svg.append("g").attr("class", "nodesContainer") initialize() function initialize() { link = linksContainer.selectAll("line") .data(data.links) .join("line") .attr("class", "line") node = nodesContainer.selectAll(".node") .data(data.nodes, d =gt; d.id) .join("g") .attr("class", "node") .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) node.selectAll("circle") .data(d =gt; [d]) .join("circle") .attr("r", setSize) .on("click", onClick) force .nodes(data.nodes) .on("tick", ticked) force .force("link") .links(data.links) } function setSize(d) { switch (d.type) { case "parent": return 40 case "child": return 20 default: return 40 } } function onClick(event, d) { const newLinks = link.filter(link =gt; link.target.id === d.id); console.log(newLinks) const newNodes = newLinks.map(link =gt; data.nodes.find(newNode =gt; newNode.id === link.source.id)) console.log(newNodes) } function ticked() { 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 ")"; }); } function dragStarted(event, d) { if (!event.active) force.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragEnded(event, d) { if (!event.active) force.alphaTarget(0); d.fx = undefined; d.fy = undefined; }
body { overflow: hidden; background: #e6e7ee; margin: 0; } circle { fill: whitesmoke; stroke: black; } line { stroke: black; }
lt;script src="https://d3js.org/d3.v7.js"gt;lt;/scriptgt;
Ответ №1:
Вам нужно понять разницу между массивом данных и выбором d3. В вашем коде link
и node
являются выборками d3, представляющими круги и линии. Они не являются репрезентациями базовых данных.
Однако они предоставляют некоторые функции, которые могут быть полезны в этом контексте. Например, вы можете использовать .each(...)
вместо forEach
для перебора элементов или .data()
для получения объектов, которые выделение d3 представляет в виде массива, поэтому link.data().map
это определенно допустимая функция.
Я реализовал то, что вы хотите, добавив свойство isVisible
к каждому узлу или ссылке, которое определяет видимость. Однако это абсолютно не лучший или единственный способ сделать это, поэтому не стесняйтесь изучать альтернативы.
var svg = d3.select("body").append("svg") .attr("width", window.innerWidth) .attr("height", window.innerHeight) var data = { "nodes": [{ "id": "A", "type": "parent" }, { "id": "B", "type": "parent" }, { "id": "C", "type": "child" }, { "id": "D", "type": "child" }, { "id": "E", "type": "child" }, ], "links": [{ "source": "A", "target": "B", "distance": 125 }, { "source": "C", "target": "A", "distance": 20 }, { "source": "D", "target": "A", "distance": 20 }, { "source": "E", "target": "B", "distance": 20 }, ] } var force = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id }).distance(function(d) { return d.distance })) .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2)) .force("charge", d3.forceManyBody().strength(-1000)) .force("collision", d3.forceCollide().radius(setSize)) var linksContainer = svg.append("g").attr("class", "linksContainer") var nodesContainer = svg.append("g").attr("class", "nodesContainer") initialize() function initialize() { const links = data.links .filter(link =gt; link.isVisible !== false); const nodes = data.nodes.filter(node =gt; node.isVisible !== false); link = linksContainer.selectAll("line") .data(links) .join("line") .attr("class", "line") node = nodesContainer.selectAll(".node") .data(nodes, d =gt; d.id) .join("g") .attr("class", "node") .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) node.selectAll("circle") .data(d =gt; [d]) .join("circle") .attr("r", setSize) .on("click", onClick) force .nodes(nodes) .on("tick", ticked) force .force("link") .links(links) } function setSize(d) { switch (d.type) { case "parent": return 40 case "child": return 20 default: return 40 } } function onClick(event, d) { link.data() .forEach(link =gt; { link.isVisible = link.target.id === d.id; }); const visibleNodeIds = [ d.id, ...link.data() .filter(l =gt; l.isVisible) .map(l =gt; l.source.id) ]; node.data() .forEach(node =gt; { node.isVisible = visibleNodeIds.includes(node.id); }); initialize(); } function ticked() { 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 ")"; }); } function dragStarted(event, d) { if (!event.active) force.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragEnded(event, d) { if (!event.active) force.alphaTarget(0); d.fx = undefined; d.fy = undefined; }
body { overflow: hidden; background: #e6e7ee; margin: 0; } circle { fill: whitesmoke; stroke: black; } line { stroke: black; }
lt;script src="https://d3js.org/d3.v7.js"gt;lt;/scriptgt;