D3 过时链接仍然可见
D3 outdated links still visible
我得到的是:
我得到了一个 D3 强制图,有两种不同的 link 类型。我确实以不同的方式想象这两种类型。 need
是一条简单的线,uses
是虚线。为此,我为 link 设置了两个不同的 CSS 类,如果单击,只需切换类型。为了最终可视化此更改,我再次调用主 initialize()
函数。
问题是什么:
只要我点击其中一个 link 并切换类型,过时的行仍然可见。我错过了如何避免这种行为的重点?我如何确定过时的线路已经消失?我很感激任何提示。
更新:
我在重新初始化之前添加了 svg.selectAll("line").remove()
。但我怀疑它的最佳做法,而且有时线条会完全消失。
var graph = {
"nodes": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
],
"links": [
{
"source": 1,
"target": 2,
"type": "uses"
},
{
"source": 2,
"target": 3,
"type": "needs"
},
{
"source": 3,
"target": 1,
"type": "needs"
}
]
}
var svg = d3.select("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {
return d.id
}).distance(80))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("collision", d3.forceCollide().radius(90))
initialize()
function initialize() {
link = svg.selectAll(".link")
.data(graph.links)
.join("line")
//.attr("class", "link")
.attr("class", function (d) {
if (d.type === "uses") {
return "uses"
} else {
return "needs"
}
})
.on("dblclick", function (event, d) {
if (d.type === "uses") {
d.type = "needs"
} else if (d.type === "needs") {
d.type = "uses"
}
svg.selectAll("line").remove()
initialize()
})
node = svg.selectAll(".node")
.data(graph.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(graph.nodes)
.join("circle")
.attr("r", 30)
.style("fill", "whitesmoke")
force
.nodes(graph.nodes)
.on("tick", ticked);
force
.force("link")
.links(graph.links)
}
function ticked() {
// update link positions
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;
});
// update node positions
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;
PosX = d.x
PosY = 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 {
height: 100%;
background: #e6e7ee;
overflow: hidden;
margin: 0px;
}
line {
stroke-width: 6px;
}
line.uses {
stroke: grey;
stroke-dasharray: 5;
}
line.needs {
stroke: black;
}
line:hover {
stroke: goldenrod;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<svg id="svg"></svg>
</body>
</html>
在细分中:
link = svg.selectAll(".link")
.data(graph.links)
.join("line")
//.attr("class", "link")
.attr("class", function (d) {
if (d.type === "uses") {
return "uses"
} else {
return "needs"
}
})
selection 是 selecting link
class,但这些行实际上有 uses
或 needs
作为 classes。您可以 select 以前的 uses
和 needs
classes:
link = svg.selectAll("line.uses,line.needs")
这将使 .join()
删除那些 classes 中未使用的行。
我得到的是:
我得到了一个 D3 强制图,有两种不同的 link 类型。我确实以不同的方式想象这两种类型。 need
是一条简单的线,uses
是虚线。为此,我为 link 设置了两个不同的 CSS 类,如果单击,只需切换类型。为了最终可视化此更改,我再次调用主 initialize()
函数。
问题是什么:
只要我点击其中一个 link 并切换类型,过时的行仍然可见。我错过了如何避免这种行为的重点?我如何确定过时的线路已经消失?我很感激任何提示。
更新:
我在重新初始化之前添加了 svg.selectAll("line").remove()
。但我怀疑它的最佳做法,而且有时线条会完全消失。
var graph = {
"nodes": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
],
"links": [
{
"source": 1,
"target": 2,
"type": "uses"
},
{
"source": 2,
"target": 3,
"type": "needs"
},
{
"source": 3,
"target": 1,
"type": "needs"
}
]
}
var svg = d3.select("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {
return d.id
}).distance(80))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("collision", d3.forceCollide().radius(90))
initialize()
function initialize() {
link = svg.selectAll(".link")
.data(graph.links)
.join("line")
//.attr("class", "link")
.attr("class", function (d) {
if (d.type === "uses") {
return "uses"
} else {
return "needs"
}
})
.on("dblclick", function (event, d) {
if (d.type === "uses") {
d.type = "needs"
} else if (d.type === "needs") {
d.type = "uses"
}
svg.selectAll("line").remove()
initialize()
})
node = svg.selectAll(".node")
.data(graph.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(graph.nodes)
.join("circle")
.attr("r", 30)
.style("fill", "whitesmoke")
force
.nodes(graph.nodes)
.on("tick", ticked);
force
.force("link")
.links(graph.links)
}
function ticked() {
// update link positions
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;
});
// update node positions
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;
PosX = d.x
PosY = 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 {
height: 100%;
background: #e6e7ee;
overflow: hidden;
margin: 0px;
}
line {
stroke-width: 6px;
}
line.uses {
stroke: grey;
stroke-dasharray: 5;
}
line.needs {
stroke: black;
}
line:hover {
stroke: goldenrod;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v6.js"></script>
</head>
<body>
<svg id="svg"></svg>
</body>
</html>
在细分中:
link = svg.selectAll(".link")
.data(graph.links)
.join("line")
//.attr("class", "link")
.attr("class", function (d) {
if (d.type === "uses") {
return "uses"
} else {
return "needs"
}
})
selection 是 selecting link
class,但这些行实际上有 uses
或 needs
作为 classes。您可以 select 以前的 uses
和 needs
classes:
link = svg.selectAll("line.uses,line.needs")
这将使 .join()
删除那些 classes 中未使用的行。