Force-Directed Graph,节点不移动并卡在地图的中心
Force-Directed Graph, nodes are not moving and stuck in the center of the map
我有一个力图,其中我的节点实际上是一个带有附加圆圈和附加文本元素的“g”元素。
我的节点有问题,因为它们在拖动时不会移动并且没有定位在链接的尖端。
如果我只将我的节点更改为简单的圆圈,那么它工作正常。但是我无法让它与“g”元素一起工作。
有人建议我使用 ".attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});"在我的 ticked() 函数中,因为“g”元素没有 cx 和 cy 值(因为它们是特定于圆圈的)。
我认为问题出在我创建 drag() 函数的方式上。
非常欢迎任何帮助
ForceGraph(){
var links = [
{src:"John",target:"Aurora"},
{src:"John",target:"Mary"},
{src:"John",target:"Erik"},
{src:"John",target:"Susan"},
{src:"John",target:"Mel"},
]
var nodes = [
{id:"John"},
{id:"Aurora"},
{id:"Mary"},
{id:"Erik"},
{id:"Susan"},
{id:"Mel"},
]
var width = this.$refs.mapFrame.clientWidth // scale to parent container
var height = this.$refs.mapFrame.clientHeight // scale to parent container
// Compute values.
var nodeId = d => d.id // given d in nodes, returns a unique identifier (string)
const N = d3.map(nodes, nodeId);
const nodeTitle = (_, i) => N[i];
const T = d3.map(nodes, nodeTitle);
// Replace the input nodes and links with mutable objects for the simulation.
nodes = nodes.map(n => Object.assign({}, n));
links = links.map(l => ({
orig: l,
source: l.src,
target: l.target
}));
// Construct the forces.
const forceNode = d3.forceManyBody();
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
forceNode.strength(-450);
forceLink.strength(1);
forceLink.distance(100)
const simulation = d3.forceSimulation(nodes)
.force(link, forceLink)
.force("charge", forceNode)
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);
const svg = d3.create("svg")
.attr("id", "svgId")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", [-width/2,-height/2, width,height])
.classed("svg-content-responsive", true)
const link = svg.append("g")
.selectAll("line")
.data(links)
.join("line").attr("stroke", "white")
.attr("stroke-width", "5")
;
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
.call(drag(simulation));
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
// .call(drag(simulation));
node.append("text")
.attr("class", "foreground")
.attr("dx", function(){return -20})
.style('font-size', 30 * 0.4 + 'px')
.text(({index: i}) => T[i])
// .call(drag(simulation))
node.attr("stroke", "grey");
})
node.call(drag(simulation));
function ticked() {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});
// .attr("cx", d => d.x)
// .attr("cy", d => d.y);
}
function drag(simulation) {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
return Object.assign(svg.node() );
}
您的 node
变量应该为空,这意味着您没有更新 tick 函数中的任何内容。这是您加入的一个简短和孤立的示例,之后我调用 selection.size()
以查看有多少项目 selected with node
:
var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
node.append("text")
node.attr("stroke", "grey");
})
console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
这是预期的,加入 returns 合并进入并更新 selections;然而,由于我们提供了一个自定义输入函数,我们需要显式 return 输入的节点(如果提供一个自定义更新函数,我们也需要这样做)。来自文档:
The selections returned by the enter and update functions are merged and then returned by selection.join
如果我们将 return node
添加到您的输入函数的末尾,我们可以看到我们的 select 离子大小现在是 3。
var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
node.append("text")
node.attr("stroke", "grey");
return node;
})
console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
现在 node
包括输入的节点,刻度函数应该按预期工作。
另一种方法是 select g
元素每个刻度用 d3.select(".circle-group")
而不是使用 node
,但是,这不是很高效。
我有一个力图,其中我的节点实际上是一个带有附加圆圈和附加文本元素的“g”元素。
我的节点有问题,因为它们在拖动时不会移动并且没有定位在链接的尖端。
如果我只将我的节点更改为简单的圆圈,那么它工作正常。但是我无法让它与“g”元素一起工作。
有人建议我使用 ".attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});"在我的 ticked() 函数中,因为“g”元素没有 cx 和 cy 值(因为它们是特定于圆圈的)。
我认为问题出在我创建 drag() 函数的方式上。
非常欢迎任何帮助
ForceGraph(){
var links = [
{src:"John",target:"Aurora"},
{src:"John",target:"Mary"},
{src:"John",target:"Erik"},
{src:"John",target:"Susan"},
{src:"John",target:"Mel"},
]
var nodes = [
{id:"John"},
{id:"Aurora"},
{id:"Mary"},
{id:"Erik"},
{id:"Susan"},
{id:"Mel"},
]
var width = this.$refs.mapFrame.clientWidth // scale to parent container
var height = this.$refs.mapFrame.clientHeight // scale to parent container
// Compute values.
var nodeId = d => d.id // given d in nodes, returns a unique identifier (string)
const N = d3.map(nodes, nodeId);
const nodeTitle = (_, i) => N[i];
const T = d3.map(nodes, nodeTitle);
// Replace the input nodes and links with mutable objects for the simulation.
nodes = nodes.map(n => Object.assign({}, n));
links = links.map(l => ({
orig: l,
source: l.src,
target: l.target
}));
// Construct the forces.
const forceNode = d3.forceManyBody();
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
forceNode.strength(-450);
forceLink.strength(1);
forceLink.distance(100)
const simulation = d3.forceSimulation(nodes)
.force(link, forceLink)
.force("charge", forceNode)
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);
const svg = d3.create("svg")
.attr("id", "svgId")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", [-width/2,-height/2, width,height])
.classed("svg-content-responsive", true)
const link = svg.append("g")
.selectAll("line")
.data(links)
.join("line").attr("stroke", "white")
.attr("stroke-width", "5")
;
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
.call(drag(simulation));
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
// .call(drag(simulation));
node.append("text")
.attr("class", "foreground")
.attr("dx", function(){return -20})
.style('font-size', 30 * 0.4 + 'px')
.text(({index: i}) => T[i])
// .call(drag(simulation))
node.attr("stroke", "grey");
})
node.call(drag(simulation));
function ticked() {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});
// .attr("cx", d => d.x)
// .attr("cy", d => d.y);
}
function drag(simulation) {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
return Object.assign(svg.node() );
}
您的 node
变量应该为空,这意味着您没有更新 tick 函数中的任何内容。这是您加入的一个简短和孤立的示例,之后我调用 selection.size()
以查看有多少项目 selected with node
:
var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
node.append("text")
node.attr("stroke", "grey");
})
console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
这是预期的,加入 returns 合并进入并更新 selections;然而,由于我们提供了一个自定义输入函数,我们需要显式 return 输入的节点(如果提供一个自定义更新函数,我们也需要这样做)。来自文档:
The selections returned by the enter and update functions are merged and then returned by selection.join
如果我们将 return node
添加到您的输入函数的末尾,我们可以看到我们的 select 离子大小现在是 3。
var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
var node = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group")
node.append("circle")
.attr("class", "background")
.style("fill", "blue")
.attr("r", 30)
node.append("text")
node.attr("stroke", "grey");
return node;
})
console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
现在 node
包括输入的节点,刻度函数应该按预期工作。
另一种方法是 select g
元素每个刻度用 d3.select(".circle-group")
而不是使用 node
,但是,这不是很高效。