问题
After following up from this question Insert text inside Circle in D3 chart
My nodes are sticking to the center. I am not sure which property is directing my nodes and their x and y coordinates. I recently chnaged my code to add a g
layer to the circles so that i can append text along with shape.
DATA
https://api.myjson.com/bins/hwtj0
UPDATED CODE
async function d3function() {
d3.selectAll("svg > *").remove();
const svg = d3.select("svg");
file = document.getElementById("selectFile").value;
console.log("File: " + file)
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');
data = (await fetch(file)).json()
d3.json(file).then(function(data) {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
console.log(links.length);
console.log(nodes.length);
const simulation = forceSimulation(nodes, links).on("tick", ticked);
var categorical = [
{ "name" : "schemeAccent", "n": 8},
{ "name" : "schemeDark2", "n": 8},
]
// var colorScale = d3.scaleOrdinal(d3[categorical[6].name])
var color = d3.scaleOrdinal(d3[categorical[1].name]);
var drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", d => Math.sqrt(d.value));
// link.append("title").text(d => d.value);
// var circles = svg.append("g")
// .attr("stroke", "#fff")
// .attr("stroke-width", 1.5)
// .selectAll(".circle")
// .data(nodes)
// const node = circles.enter().append("circle")
// .attr("r", 5)
// .attr("fill", d => color(d.group))
// .call(drag(simulation));
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circles")
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
node.append("circle")
.classed('circle', true)
.attr("r", 5)
.attr("fill", d => color(d.group))
.call(drag(simulation));
node
.append("text")
.classed('circleText', true)
.attr('dy', '0.35em')
.attr('dx', 5)
.text(d => "Node: " + d.id);
node.append("title").text(d => "Node: " + d.id);
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("cx", d => d.x)
.attr("cy", d => d.y);
}
});
}
function forceSimulation(nodes, links) {
return d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter());
}
UPDATED OUTPUT
EXPECTED OUTPUT
UPDATED HTML
<g stroke="#fff" stroke-width="1.5">
<g class="circle" cx="-35.89111508769784" cy="131.13965804447696">
<circle class="circle" r="5" fill="#1b9e77"></circle>
<text class="circleText" dy="0.35em" dx="5">Node: 0</text>
<title>Node: 0</title>
</g>
<g class="circle" cx="70.97799024729613" cy="-195.71408429254427">
<circle class="circle" r="5" fill="#d95f02"></circle>
<text class="circleText" dy="0.35em" dx="5">Node: 3</text>
<title>Node: 3</title>
</g>
[....]
</g>
回答1:
You have to adapt your code slightly as it currently assumes that you're working with circle
elements, where you specify the centres using cx
and cy
, but you are now using g
elements, which use standard x
and y
coordinates.
First, remove the transform from the g
element (that's a leftover from my demo code):
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll(".circles") // note - should be .circles!
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
and in the ticked()
function, change the node
updating code into a transform that works on g
elements (which don't have cx
or cy
):
node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')' )
Demo:
var json = {"nodes":[{"id":"0","group":0},{"id":"1","group":1},{"id":"2","group":2},{"id":"3","group":3},{"id":"4","group":4},{"id":"5","group":5},{"id":"6","group":6},{"id":"7","group":7},{"id":"8","group":8},{"id":"9","group":9},{"id":"10","group":10},{"id":"11","group":11},{"id":"12","group":12},{"id":"13","group":13},{"id":"14","group":14},{"id":"15","group":15},{"id":"16","group":16},{"id":"17","group":17},{"id":"18","group":18},{"id":"19","group":19}],"links":[{"source":"0","target":"1","value":1},{"source":"0","target":"18","value":1},{"source":"0","target":"10","value":1},{"source":"0","target":"12","value":1},{"source":"0","target":"5","value":1},{"source":"0","target":"8","value":1},{"source":"1","target":"0","value":1},{"source":"1","target":"9","value":1},{"source":"1","target":"4","value":1},{"source":"2","target":"4","value":1},{"source":"2","target":"17","value":1},{"source":"2","target":"13","value":1},{"source":"2","target":"15","value":1},{"source":"3","target":"6","value":1},{"source":"4","target":"14","value":1},{"source":"4","target":"2","value":1},{"source":"4","target":"5","value":1},{"source":"4","target":"19","value":1},{"source":"4","target":"1","value":1},{"source":"5","target":"4","value":1},{"source":"5","target":"0","value":1},{"source":"6","target":"3","value":1},{"source":"7","target":"18","value":1},{"source":"7","target":"16","value":1},{"source":"8","target":"0","value":1},{"source":"9","target":"1","value":1},{"source":"10","target":"0","value":1},{"source":"10","target":"15","value":1},{"source":"12","target":"0","value":1},{"source":"13","target":"15","value":1},{"source":"13","target":"2","value":1},{"source":"14","target":"4","value":1},{"source":"15","target":"13","value":1},{"source":"15","target":"10","value":1},{"source":"15","target":"2","value":1},{"source":"16","target":"7","value":1},{"source":"17","target":"2","value":1},{"source":"18","target":"0","value":1},{"source":"18","target":"7","value":1},{"source":"19","target":"4","value":1},{"source":"19","target":"4","value":1}]};
d3.selectAll("svg > *").remove();
const svg = d3.select("svg");
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');
const links = json.links.map(d => Object.create(d));
const nodes = json.nodes.map(d => Object.create(d));
const simulation = forceSimulation(nodes, links).on("tick", ticked);
var categorical = [
{
"name": "schemeAccent",
"n": 8
},
{
"name": "schemeDark2",
"n": 8
}, ]
var color = d3.scaleOrdinal(d3[categorical[1].name]);
var drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", d => Math.sqrt(d.value));
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll(".circles")
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
.call(drag(simulation))
// .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
const circle = node.append("circle")
.classed('circle', true)
.attr("r", 5)
.attr("fill", d => color(d.group))
node
.append("text")
.classed('circleText', true)
.attr('dy', '0.35em')
.attr('dx', 5)
.text(d => "Node: " + d.id);
node.append("title").text(d => "Node: " + d.id);
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', d => 'translate(' + d.x + ',' + d.y + ')')
}
function forceSimulation(nodes, links) {
return d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter());
}
.circleText { fill: black; stroke: none }
<script src="//d3js.org/d3.v5.js"></script>
<svg></svg>
来源:https://stackoverflow.com/questions/52804351/force-directed-graphs-nodes-stick-to-the-center