我应该如何删除 d3-force 中的节点?
How should I remove nodes in d3-force?
我想在点击这个节点时删除节点,所以我只是在点击nodes = nodes.filter(v => v.id !== d.id)
时删除了节点。实际上,第一次可以,但是接下来就不行了。
这是我的代码:
uuid = function uuid() {
var template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return template.replace(/[xy]/g, c => {
var r = (Math.random() * 16) | 0;
if (c === 'y') r = (r & 3) | 8;
return r.toString(16);
});
}
const width = 800;
const height = 400;
const count = 10;
let nodes = Array.from({length: count}).map((_, i) => ({x: width / count * i + 20, y: height / 2, id: uuid()}));
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg
.selectAll(".node");
const simulation = d3
.forceSimulation()
.nodes(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
function update() {
node = node.data(nodes, d=> d.id)
.join("g")
.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";})
.on("click", (ev, d) => {
nodes = nodes.filter(v => v.id !== d.id);
update();
});
simulation.nodes(nodes);
if (simulation.alpha() <= 1) {
simulation.alpha(1);
simulation.restart();
}
}
update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
我不知道问题出在哪里,但请查看您的代码生成的 DOM。
有一个g
个元素的列表,每个g
包含很多嵌套的circle
。很奇怪,也说不通。
所以我建议你重构你的代码,使其具有类似的东西:
<svg>
<circle class="node#1"... />
<circle class="node#2"... />
<circle class="node#3"... />
<circle class="node#4"... />
</svg>
然后,当使用点击圆形元素时,获取 class 名称并删除该节点
您只想为新节点附加一个圆圈,对于任何其他节点,附加一个圆圈意味着您下次单击时,实际上有两个圆圈要删除,一个都没有!
.join()
可以传递三个函数,您可以将其应用于 enter
、update
和 exit
选择。这些被执行,return 值被加入,然后你可以对所有选择组合进行操作。
在这种情况下,我将所有一次性 circle
逻辑移至 enter
部分,注意不要 return circle
,而是 g
您首先附加的元素。如果您 return circle
,它会将这些与现有的 `g 元素结合起来,逻辑就会中断。
uuid = function uuid() {
var template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return template.replace(/[xy]/g, c => {
var r = (Math.random() * 16) | 0;
if (c === 'y') r = (r & 3) | 8;
return r.toString(16);
});
}
const width = 800;
const height = 400;
const count = 10;
let nodes = Array.from({length: count}).map((_, i) => ({x: width / count * i + 20, y: height / 2, id: uuid()}));
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg
.selectAll(".node");
const simulation = d3
.forceSimulation()
.nodes(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
function update() {
console.log(nodes.length);
node = node.data(nodes, d=> d.id)
.join(
enter => {
const g = enter
.append("g")
.attr("class", "node");
g
.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
return g;
},
update => update,
exit => exit.remove()
)
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";})
.on("click", (ev, d) => {
nodes = nodes.filter(v => v.id !== d.id);
update();
});
simulation.nodes(nodes);
if (simulation.alpha() <= 1) {
simulation.alpha(1);
simulation.restart();
}
}
update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
我想在点击这个节点时删除节点,所以我只是在点击nodes = nodes.filter(v => v.id !== d.id)
时删除了节点。实际上,第一次可以,但是接下来就不行了。
这是我的代码:
uuid = function uuid() {
var template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return template.replace(/[xy]/g, c => {
var r = (Math.random() * 16) | 0;
if (c === 'y') r = (r & 3) | 8;
return r.toString(16);
});
}
const width = 800;
const height = 400;
const count = 10;
let nodes = Array.from({length: count}).map((_, i) => ({x: width / count * i + 20, y: height / 2, id: uuid()}));
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg
.selectAll(".node");
const simulation = d3
.forceSimulation()
.nodes(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
function update() {
node = node.data(nodes, d=> d.id)
.join("g")
.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px")
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";})
.on("click", (ev, d) => {
nodes = nodes.filter(v => v.id !== d.id);
update();
});
simulation.nodes(nodes);
if (simulation.alpha() <= 1) {
simulation.alpha(1);
simulation.restart();
}
}
update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>
我不知道问题出在哪里,但请查看您的代码生成的 DOM。
有一个g
个元素的列表,每个g
包含很多嵌套的circle
。很奇怪,也说不通。
所以我建议你重构你的代码,使其具有类似的东西:
<svg>
<circle class="node#1"... />
<circle class="node#2"... />
<circle class="node#3"... />
<circle class="node#4"... />
</svg>
然后,当使用点击圆形元素时,获取 class 名称并删除该节点
您只想为新节点附加一个圆圈,对于任何其他节点,附加一个圆圈意味着您下次单击时,实际上有两个圆圈要删除,一个都没有!
.join()
可以传递三个函数,您可以将其应用于 enter
、update
和 exit
选择。这些被执行,return 值被加入,然后你可以对所有选择组合进行操作。
在这种情况下,我将所有一次性 circle
逻辑移至 enter
部分,注意不要 return circle
,而是 g
您首先附加的元素。如果您 return circle
,它会将这些与现有的 `g 元素结合起来,逻辑就会中断。
uuid = function uuid() {
var template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return template.replace(/[xy]/g, c => {
var r = (Math.random() * 16) | 0;
if (c === 'y') r = (r & 3) | 8;
return r.toString(16);
});
}
const width = 800;
const height = 400;
const count = 10;
let nodes = Array.from({length: count}).map((_, i) => ({x: width / count * i + 20, y: height / 2, id: uuid()}));
const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg
.selectAll(".node");
const simulation = d3
.forceSimulation()
.nodes(nodes)
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();
function update() {
console.log(nodes.length);
node = node.data(nodes, d=> d.id)
.join(
enter => {
const g = enter
.append("g")
.attr("class", "node");
g
.append("circle")
.attr("r", 12)
.attr("cursor", "move")
.attr("fill", "#ccc")
.attr("stroke", "#000")
.attr("stroke-width", "1.5px");
return g;
},
update => update,
exit => exit.remove()
)
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";})
.on("click", (ev, d) => {
nodes = nodes.filter(v => v.id !== d.id);
update();
});
simulation.nodes(nodes);
if (simulation.alpha() <= 1) {
simulation.alpha(1);
simulation.restart();
}
}
update()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>